Skip to content

Commit

Permalink
feat(selector): add methods/props
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Jul 22, 2024
1 parent d68573d commit 3dca778
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 107 deletions.
4 changes: 3 additions & 1 deletion app/src/main/kotlin/li/songe/gkd/data/ResolvedRule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,9 @@ fun clearNodeCache(t: Long = System.currentTimeMillis()) {
lastCacheTime = t
if (BuildConfig.DEBUG) {
val sizeList = defaultCacheTransform.cache.sizeList
Log.d("cache", "clear cache, sizeList=$sizeList")
if (sizeList.any { it > 0 }) {
Log.d("cache", "clear cache, sizeList=$sizeList")
}
}
defaultTransform.cache.clear()
defaultCacheTransform.cache.clear()
Expand Down
22 changes: 9 additions & 13 deletions app/src/main/kotlin/li/songe/gkd/service/AbExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,7 @@ val getChildren: (AccessibilityNodeInfo) -> Sequence<AccessibilityNodeInfo> = {
}

private val typeInfo by lazy {
initDefaultTypeInfo().apply {
nodeType.props = nodeType.props.filter { !it.name.startsWith('_') }.toTypedArray()
contextType.props = contextType.props.filter { !it.name.startsWith('_') }.toTypedArray()
}.contextType
initDefaultTypeInfo().globalType
}

fun Selector.checkSelector(): String? {
Expand Down Expand Up @@ -357,6 +354,7 @@ fun createCacheTransform(): CacheTransform {
when (target) {
is Context<*> -> when (name) {
"prev" -> target.prev
"current" -> target.current
else -> getNodeAttr(target.current as AccessibilityNodeInfo, name)
}

Expand All @@ -369,7 +367,7 @@ fun createCacheTransform(): CacheTransform {
when (target) {
is AccessibilityNodeInfo -> when (name) {
"getChild" -> {
args.getIntOrNull()?.let { index ->
args.getInt().let { index ->
cache.getChild(target, index)
}
}
Expand All @@ -379,11 +377,11 @@ fun createCacheTransform(): CacheTransform {

is Context<*> -> when (name) {
"getPrev" -> {
args.getIntOrNull()?.let { target.getPrev(it) }
args.getInt().let { target.getPrev(it) }
}

"getChild" -> {
args.getIntOrNull()?.let { index ->
args.getInt().let { index ->
cache.getChild(target.current as AccessibilityNodeInfo, index)
}
}
Expand Down Expand Up @@ -545,9 +543,7 @@ fun createCacheTransform(): CacheTransform {
return CacheTransform(transform, cache)
}

private fun List<Any>.getIntOrNull(i: Int = 0): Int? {
return getOrNull(i) as? Int ?: return null
}
private fun List<Any>.getInt(i: Int = 0) = get(i) as Int

fun createNoCacheTransform(): CacheTransform {
val cache = NodeCache()
Expand All @@ -569,7 +565,7 @@ fun createNoCacheTransform(): CacheTransform {
when (target) {
is AccessibilityNodeInfo -> when (name) {
"getChild" -> {
args.getIntOrNull()?.let { index ->
args.getInt().let { index ->
cache.getChild(target, index)
}
}
Expand All @@ -579,11 +575,11 @@ fun createNoCacheTransform(): CacheTransform {

is Context<*> -> when (name) {
"getPrev" -> {
args.getIntOrNull()?.let { target.getPrev(it) }
args.getInt().let { target.getPrev(it) }
}

"getChild" -> {
args.getIntOrNull()?.let { index ->
args.getInt().let { index ->
cache.getChild(target.current as AccessibilityNodeInfo, index)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,18 @@ sealed class CompareOperator(val key: String) : Stringify {
): Boolean {
val left = leftExp.getAttr(context, transform)
val right = rightExp.getAttr(context, transform)
return compare(left, right)
}

override fun allowType(left: ValueExpression, right: ValueExpression) = true

fun compare(left: Any?, right: Any?): Boolean {
return if (left is CharSequence && right is CharSequence) {
left.contentReversedEquals(right)
} else {
left == right
}
}

override fun allowType(left: ValueExpression, right: ValueExpression) = true
}

data object NotEqual : CompareOperator("!=") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ data class UnknownMemberMethodException(
@JsExport
data class MismatchParamTypeException(
val call: ValueExpression.CallExpression,
val argument: ValueExpression.LiteralExpression,
val argument: ValueExpression,
val type: PrimitiveType
) : SelectorCheckException("Mismatch Param Type: ${argument.value} should be ${type.key}")

Expand Down
137 changes: 95 additions & 42 deletions selector/src/commonMain/kotlin/li/songe/selector/Selector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -138,76 +138,129 @@ private fun getExpType(exp: ValueExpression, typeInfo: TypeInfo): PrimitiveType?
is ValueExpression.BooleanLiteral -> PrimitiveType.BooleanType
is ValueExpression.IntLiteral -> PrimitiveType.IntType
is ValueExpression.StringLiteral -> PrimitiveType.StringType
is ValueExpression.Variable -> checkVariable(exp, typeInfo).type
is ValueExpression.Variable -> checkVariable(exp, typeInfo, typeInfo).type
}
}

private fun checkVariable(value: ValueExpression.Variable, typeInfo: TypeInfo): TypeInfo {
private fun checkMethod(
method: MethodInfo,
value: ValueExpression.CallExpression,
globalTypeInfo: TypeInfo
): TypeInfo {
method.params.forEachIndexed { index, argTypeInfo ->
when (val argExp = value.arguments[index]) {
is ValueExpression.NullLiteral -> {}
is ValueExpression.BooleanLiteral -> {
if (argTypeInfo.type != PrimitiveType.BooleanType) {
throw MismatchParamTypeException(
value,
argExp,
PrimitiveType.BooleanType
)
}
}

is ValueExpression.IntLiteral -> {
if (argTypeInfo.type != PrimitiveType.IntType) {
throw MismatchParamTypeException(value, argExp, PrimitiveType.IntType)
}
}

is ValueExpression.StringLiteral -> {
if (argTypeInfo.type != PrimitiveType.StringType) {
throw MismatchParamTypeException(
value,
argExp,
PrimitiveType.StringType
)
}
}

is ValueExpression.Variable -> {
val type = checkVariable(argExp, argTypeInfo, globalTypeInfo)
if (type.type != argTypeInfo.type) {
throw MismatchParamTypeException(
value,
argExp,
type.type
)
}
}
}
}
return method.returnType
}

private fun checkVariable(
value: ValueExpression.Variable,
currentTypeInfo: TypeInfo,
globalTypeInfo: TypeInfo,
): TypeInfo {
return when (value) {
is ValueExpression.CallExpression -> {
val method = when (value.callee) {
val methods = when (value.callee) {
is ValueExpression.CallExpression -> {
throw IllegalArgumentException("Unsupported nested call")
}

is ValueExpression.Identifier -> {
// getChild(0)
typeInfo.methods.find { it.name == value.callee.value && it.params.size == value.arguments.size }
?: throw UnknownIdentifierMethodException(value.callee)
globalTypeInfo.methods.filter { it.name == value.callee.value && it.params.size == value.arguments.size }
.apply {
if (isEmpty()) {
throw UnknownIdentifierMethodException(value.callee)
}
}
}

is ValueExpression.MemberExpression -> {
// parent.getChild(0)
checkVariable(
value.callee.object0, typeInfo
).methods.find { it.name == value.callee.property && it.params.size == value.arguments.size }
?: throw UnknownMemberMethodException(value.callee)
value.callee.object0,
currentTypeInfo,
globalTypeInfo
).methods.filter { it.name == value.callee.property && it.params.size == value.arguments.size }
.apply {
if (isEmpty()) {
throw UnknownMemberMethodException(value.callee)
}
}
}
}
method.params.forEachIndexed { index, argTypeInfo ->
when (val argExp = value.arguments[index]) {
is ValueExpression.NullLiteral -> {}
is ValueExpression.BooleanLiteral -> {
if (argTypeInfo.type != PrimitiveType.BooleanType) {
throw MismatchParamTypeException(
value,
argExp,
PrimitiveType.BooleanType
)
}
}

is ValueExpression.IntLiteral -> {
if (argTypeInfo.type != PrimitiveType.IntType) {
throw MismatchParamTypeException(value, argExp, PrimitiveType.IntType)
}
}

is ValueExpression.StringLiteral -> {
if (argTypeInfo.type != PrimitiveType.StringType) {
throw MismatchParamTypeException(
value,
argExp,
PrimitiveType.StringType
)
}
}

is ValueExpression.Variable -> {
checkVariable(argExp, argTypeInfo)
if (methods.size == 1) {
checkMethod(methods[0], value, globalTypeInfo)
return methods[0].returnType
}
methods.forEachIndexed { i, method ->
try {
checkMethod(method, value, globalTypeInfo)
return method.returnType
} catch (e: SelectorCheckException) {
if (i == methods.size - 1) {
throw e
}
// ignore
}
}
return method.returnType
if (value.callee is ValueExpression.Identifier) {
throw UnknownIdentifierMethodException(value.callee)
} else if (value.callee is ValueExpression.MemberExpression) {
throw UnknownMemberMethodException(value.callee)
}
throw IllegalArgumentException("Unsupported nested call")
}

is ValueExpression.Identifier -> {
typeInfo.props.find { it.name == value.value }?.type
globalTypeInfo.props.find { it.name == value.value }?.type
?: throw UnknownIdentifierException(value)
}

is ValueExpression.MemberExpression -> {
checkVariable(value.object0, typeInfo).props.find { it.name == value.property }?.type
checkVariable(
value.object0,
currentTypeInfo,
globalTypeInfo
).props.find { it.name == value.property }?.type
?: throw UnknownMemberException(value)
}
}
Expand Down
16 changes: 14 additions & 2 deletions selector/src/commonMain/kotlin/li/songe/selector/TypeInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ data class MethodInfo(
val name: String,
val returnType: TypeInfo,
val params: Array<TypeInfo> = emptyArray(),
) {
) : Stringify {
override fun stringify(): String {
return "$name(${params.joinToString(", ") { it.stringify() }}): ${returnType.stringify()}"
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
Expand Down Expand Up @@ -48,7 +52,15 @@ data class TypeInfo(
val type: PrimitiveType,
var props: Array<PropInfo> = arrayOf(),
var methods: Array<MethodInfo> = arrayOf(),
) {
) : Stringify {
override fun stringify(): String {
return if (type is PrimitiveType.ObjectType) {
type.name
} else {
type.key
}
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
Expand Down
Loading

0 comments on commit 3dca778

Please sign in to comment.