Skip to content

Commit

Permalink
parallelism PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
Axelen123 committed Nov 10, 2024
1 parent 2ecec37 commit a899a52
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 174 deletions.
159 changes: 119 additions & 40 deletions src/main/kotlin/app/revanced/patcher/Fingerprint.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,93 @@ import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.StringReference
import com.android.tools.smali.dexlib2.util.MethodUtil
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.job
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select
import kotlinx.coroutines.withContext

/*
suspend inline fun <T, R> Iterable<T>.concurrentFirstNotNullOfOrNull(crossinline transform: (T) -> R?) =
asFlow().flatMapMerge { value ->
flow { transform(value)?.let { emit(it) } }
}.firstOrNull()
*/
/*
suspend inline fun <T> Iterable<T>.concurrentFind(crossinline predicate: (T) -> Boolean) =
asFlow().flatMapMerge { value -> flow { if (predicate(value)) emit(value) } }.firstOrNull()
*/
internal fun List<*>.chunks(count: Int): List<Pair<Int, Int>> {
if (size <= count) return listOf(0 to lastIndex)

val chunkSize = size / count
val indices = MutableList(count) { (it*chunkSize) + 1 to (it + 1)*chunkSize }
indices[0] = 0 to indices[0].second
indices[indices.lastIndex] = indices[indices.lastIndex].first to lastIndex
return indices
}
internal suspend inline fun <T> List<T>.concurrentFind(crossinline predicate: (T) -> Boolean): T? = coroutineScope {
val cpus = Runtime.getRuntime().availableProcessors()
val completableDeferred = CompletableDeferred<T?>(parent = coroutineContext.job)
val jobs = chunks(cpus).map { (start, end) ->
launch(Dispatchers.Default) {
var i = start
while (i <= end) {
val element = this@concurrentFind[i]
if (predicate(element)) {
completableDeferred.complete(element)
return@launch
}
i++
}
}
}
val notFoundJob = launch {
jobs.joinAll()
completableDeferred.complete(null)
}

val result = completableDeferred.await()
jobs.forEach(Job::cancel)
notFoundJob.cancel()
result
}

internal suspend inline fun <T, R> List<T>.concurrentFirstNotNullOfOrNull(crossinline transform: (T) -> R?): R? = coroutineScope {
val cpus = Runtime.getRuntime().availableProcessors()
val completableDeferred = CompletableDeferred<R?>(parent = coroutineContext.job)
val jobs = chunks(cpus).map { (start, end) ->
launch(Dispatchers.Default) {
var i = start
while (i <= end) {
val element = this@concurrentFirstNotNullOfOrNull[i]
transform(element)?.let { value ->
completableDeferred.complete(value)
return@launch
}
i++
}
}
}
val notFoundJob = launch {
jobs.joinAll()
completableDeferred.complete(null)
}

val result = completableDeferred.await()
jobs.forEach(Job::cancel)
notFoundJob.cancel()
result
}

/**
* A fingerprint for a method. A fingerprint is a partial description of a method.
Expand Down Expand Up @@ -51,9 +138,11 @@ class Fingerprint internal constructor(
/**
* The match for this [Fingerprint]. Null if unmatched.
*/
/*
context(BytecodePatchContext)
private val matchOrNull: Match?
get() = matchOrNull()
*/

/**
* Match using [BytecodePatchContext.lookupMaps].
Expand All @@ -69,10 +158,10 @@ class Fingerprint internal constructor(
* @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise.
*/
context(BytecodePatchContext)
internal fun matchOrNull(): Match? {
internal suspend fun matchOrNull(): Match? {
if (_matchOrNull != null) return _matchOrNull

var match = strings?.mapNotNull {
val match = strings?.mapNotNull {
lookupMaps.methodsByStrings[it]
}?.minByOrNull { it.size }?.let { methodClasses ->
methodClasses.forEach { (classDef, method) ->
Expand All @@ -84,14 +173,9 @@ class Fingerprint internal constructor(
}
if (match != null) return match

synchronized(classes) {
classes.forEach { classDef ->
match = matchOrNull(classDef)
if (match != null) return match
}
return withContext(Dispatchers.Default) {
classes.concurrentFirstNotNullOfOrNull<ClassDef, Match> { matchOrNull(it) }
}

return null
}

/**
Expand Down Expand Up @@ -122,7 +206,7 @@ class Fingerprint internal constructor(
* @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise.
*/
context(BytecodePatchContext)
fun matchOrNull(
suspend fun matchOrNull(
method: Method,
) = matchOrNull(method, classBy { method.definingClass == it.type }!!.immutableClass)

Expand Down Expand Up @@ -184,7 +268,8 @@ class Fingerprint internal constructor(
return@forEachIndexed
}

val string = ((instruction as ReferenceInstruction).reference as StringReference).string
val string =
((instruction as ReferenceInstruction).reference as StringReference).string
val index = stringsList.indexOfFirst(string::contains)
if (index == -1) return@forEachIndexed

Expand Down Expand Up @@ -261,8 +346,7 @@ class Fingerprint internal constructor(
* @throws PatchException If the [Fingerprint] has not been matched.
*/
context(BytecodePatchContext)
private val match
get() = matchOrNull ?: throw exception
private suspend fun match() = matchOrNull() ?: throw exception

/**
* Match using a [ClassDef].
Expand All @@ -285,7 +369,7 @@ class Fingerprint internal constructor(
* @throws PatchException If the fingerprint has not been matched.
*/
context(BytecodePatchContext)
fun match(
suspend fun match(
method: Method,
) = matchOrNull(method) ?: throw exception

Expand All @@ -307,15 +391,13 @@ class Fingerprint internal constructor(
* The class the matching method is a member of.
*/
context(BytecodePatchContext)
val originalClassDefOrNull
get() = matchOrNull?.originalClassDef
suspend fun originalClassDefOrNull() = matchOrNull()?.originalClassDef

/**
* The matching method.
*/
context(BytecodePatchContext)
val originalMethodOrNull
get() = matchOrNull?.originalMethod
suspend fun originalMethodOrNull() = matchOrNull()?.originalMethod

/**
* The mutable version of [originalClassDefOrNull].
Expand All @@ -324,8 +406,7 @@ class Fingerprint internal constructor(
* Use [originalClassDefOrNull] if mutable access is not required.
*/
context(BytecodePatchContext)
val classDefOrNull
get() = matchOrNull?.classDef
suspend fun classDefOrNull() = matchOrNull()?.classDef

/**
* The mutable version of [originalMethodOrNull].
Expand All @@ -334,40 +415,35 @@ class Fingerprint internal constructor(
* Use [originalMethodOrNull] if mutable access is not required.
*/
context(BytecodePatchContext)
val methodOrNull
get() = matchOrNull?.method
suspend fun methodOrNull() = matchOrNull()?.method

/**
* The match for the opcode pattern.
*/
context(BytecodePatchContext)
val patternMatchOrNull
get() = matchOrNull?.patternMatch
suspend fun patternMatchOrNull() = matchOrNull()?.patternMatch

/**
* The matches for the strings.
*/
context(BytecodePatchContext)
val stringMatchesOrNull
get() = matchOrNull?.stringMatches
suspend fun stringMatchesOrNull() = matchOrNull()?.stringMatches

/**
* The class the matching method is a member of.
*
* @throws PatchException If the fingerprint has not been matched.
*/
context(BytecodePatchContext)
val originalClassDef
get() = match.originalClassDef
suspend fun originalClassDef() = match().originalClassDef

/**
* The matching method.
*
* @throws PatchException If the fingerprint has not been matched.
*/
context(BytecodePatchContext)
val originalMethod
get() = match.originalMethod
suspend fun originalMethod() = match().originalMethod

/**
* The mutable version of [originalClassDef].
Expand All @@ -378,8 +454,7 @@ class Fingerprint internal constructor(
* @throws PatchException If the fingerprint has not been matched.
*/
context(BytecodePatchContext)
val classDef
get() = match.classDef
suspend fun classDef() = match().classDef

/**
* The mutable version of [originalMethod].
Expand All @@ -390,26 +465,23 @@ class Fingerprint internal constructor(
* @throws PatchException If the fingerprint has not been matched.
*/
context(BytecodePatchContext)
val method
get() = match.method
suspend fun method() = match().method

/**
* The match for the opcode pattern.
*
* @throws PatchException If the fingerprint has not been matched.
*/
context(BytecodePatchContext)
val patternMatch
get() = match.patternMatch
suspend fun patternMatch() = match().patternMatch

/**
* The matches for the strings.
*
* @throws PatchException If the fingerprint has not been matched.
*/
context(BytecodePatchContext)
val stringMatches
get() = match.stringMatches
suspend fun stringMatches() = match().stringMatches
}

/**
Expand All @@ -433,15 +505,22 @@ class Match internal constructor(
* Accessing this property allocates a [ClassProxy].
* Use [originalClassDef] if mutable access is not required.
*/
val classDef by lazy { proxy(originalClassDef).mutableClass }
val classDef by lazy { syncProxy(originalClassDef).mutableClass }

/**
* The mutable version of [originalMethod].
*
* Accessing this property allocates a [ClassProxy].
* Use [originalMethod] if mutable access is not required.
*/
val method by lazy { classDef.methods.first { MethodUtil.methodSignaturesMatch(it, originalMethod) } }
val method by lazy {
classDef.methods.first {
MethodUtil.methodSignaturesMatch(
it,
originalMethod
)
}
}

/**
* A match for an opcode pattern.
Expand Down
Loading

0 comments on commit a899a52

Please sign in to comment.