mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2026-01-11 13:56:16 +00:00
fix: More bugfixes, patches now compile
This commit is contained in:
@@ -5,9 +5,11 @@ package app.revanced.patcher
|
|||||||
import app.revanced.patcher.InstructionLocation.*
|
import app.revanced.patcher.InstructionLocation.*
|
||||||
import app.revanced.patcher.Match.PatternMatch
|
import app.revanced.patcher.Match.PatternMatch
|
||||||
import app.revanced.patcher.Matcher.MatchContext
|
import app.revanced.patcher.Matcher.MatchContext
|
||||||
import app.revanced.patcher.extensions.*
|
import app.revanced.patcher.extensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.instructionsOrNull
|
||||||
import app.revanced.patcher.extensions.opcode
|
import app.revanced.patcher.extensions.opcode
|
||||||
import app.revanced.patcher.extensions.string
|
import app.revanced.patcher.extensions.string
|
||||||
|
import app.revanced.patcher.extensions.stringReference
|
||||||
import app.revanced.patcher.patch.BytecodePatchContext
|
import app.revanced.patcher.patch.BytecodePatchContext
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
@@ -15,9 +17,6 @@ import com.android.tools.smali.dexlib2.Opcode
|
|||||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,110 +68,69 @@ class Fingerprint internal constructor(
|
|||||||
fun matchOrNull(): Match? {
|
fun matchOrNull(): Match? {
|
||||||
if (_matchOrNull != null) return _matchOrNull
|
if (_matchOrNull != null) return _matchOrNull
|
||||||
|
|
||||||
val matchStrings = indexedMatcher<Instruction> {
|
var stringMatches: List<Match.StringMatch>? = null
|
||||||
strings?.forEach { add { string { contains(it) } } }
|
|
||||||
}
|
|
||||||
|
|
||||||
val matchIndices = indexedMatcher<Instruction>()
|
val matchIndices = indexedMatcher<Instruction>()
|
||||||
|
|
||||||
val match = context(_: MatchContext) fun Method.(): Boolean {
|
// This line is needed, because the method must be passed by reference to "matchIndices".
|
||||||
|
// Referencing the method directly would "hardcode" it in the cached pattern by value.
|
||||||
|
// By using this variable, the reference can be updated for each method.
|
||||||
|
lateinit var currentMethod: Method
|
||||||
|
|
||||||
|
context(_: MatchContext)
|
||||||
|
fun Method.match(): Boolean {
|
||||||
if (this@Fingerprint.accessFlags != null && this@Fingerprint.accessFlags != accessFlags)
|
if (this@Fingerprint.accessFlags != null && this@Fingerprint.accessFlags != accessFlags)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (this@Fingerprint.returnType != null && this@Fingerprint.returnType != returnType)
|
if (this@Fingerprint.returnType != null && !returnType.startsWith(this@Fingerprint.returnType))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (this@Fingerprint.parameters != null && !parametersStartsWith(parameterTypes, this@Fingerprint.parameters))
|
if (this@Fingerprint.parameters != null && !parametersStartsWith(
|
||||||
|
parameterTypes,
|
||||||
|
this@Fingerprint.parameters
|
||||||
|
)
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (custom != null && !custom(this, context.lookupMaps.classDefsByType[definingClass]!!))
|
if (custom != null && !custom(this, context.lookupMaps.classDefsByType[definingClass]!!))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (strings != null && !matchStrings(instructionsOrNull ?: return false))
|
stringMatches = if (strings != null) {
|
||||||
return false
|
val instructions = instructionsOrNull ?: return false
|
||||||
|
var stringsList: MutableList<String>? = null
|
||||||
|
|
||||||
fun InstructionFilter.evaluate(instruction: Instruction): Boolean {
|
buildList {
|
||||||
return when (this) {
|
instructions.forEachIndexed { instructionIndex, instruction ->
|
||||||
is AnyInstruction -> filters.any { evaluate(instruction) }
|
if (stringsList == null) stringsList = strings.toMutableList()
|
||||||
is CheckCastFilter -> instruction.opcode(Opcode.CHECK_CAST) && instruction.typeReference?.endsWith(
|
|
||||||
typeValue
|
|
||||||
) ?: false
|
|
||||||
|
|
||||||
is FieldAccessFilter -> {
|
val string = instruction.stringReference?.string ?: return@forEachIndexed
|
||||||
val reference = instruction.fieldReference ?: return false
|
val index = stringsList.indexOfFirst(string::contains)
|
||||||
|
if (index < 0) return@forEachIndexed
|
||||||
|
|
||||||
if (name != null && reference.name != name) return false
|
add(Match.StringMatch(string, instructionIndex))
|
||||||
if (type != null && !reference.type.startsWith(type)) return false
|
stringsList.removeAt(index)
|
||||||
|
|
||||||
definingClass == null || reference.definingClass.endsWith(definingClass) ||
|
|
||||||
(definingClass == "this" && reference.definingClass != method.definingClass)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is LiteralFilter -> instruction.wideLiteral == literalValue
|
if (stringsList == null || stringsList.isNotEmpty()) return false
|
||||||
&& opcodes?.contains(instruction.opcode) ?: true
|
|
||||||
|
|
||||||
is MethodCallFilter -> {
|
|
||||||
val reference = instruction.methodReference ?: return false
|
|
||||||
|
|
||||||
if (name != null && reference.name != name) return false
|
|
||||||
|
|
||||||
if (returnType != null && !reference.returnType.startsWith(returnType)) return false
|
|
||||||
|
|
||||||
if (parameters != null && !parametersStartsWith(reference.parameterTypes, parameters))
|
|
||||||
return false
|
|
||||||
|
|
||||||
if ((definingClass != null && !reference.definingClass.endsWith(definingClass)) ||
|
|
||||||
(definingClass == "this" && reference.definingClass != method.definingClass)
|
|
||||||
) return false
|
|
||||||
|
|
||||||
opcodes?.contains(instruction.opcode) ?: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is NewInstanceFilter -> {
|
} else null
|
||||||
if (opcodes != null && !opcodes.contains(instruction.opcode)) return false
|
|
||||||
instruction.reference<TypeReference> { endsWith(type) }
|
|
||||||
}
|
|
||||||
|
|
||||||
is OpcodeFilter -> instruction.opcode(opcode)
|
currentMethod = this
|
||||||
is StringFilter -> {
|
return filters == null || matchIndices(instructionsOrNull ?: return false, "match") {
|
||||||
val string = instruction.stringReference?.string ?: return false
|
|
||||||
|
|
||||||
val filterString = stringValue
|
|
||||||
when (comparison) {
|
|
||||||
StringComparisonType.EQUALS -> string == filterString
|
|
||||||
StringComparisonType.CONTAINS -> string.contains(filterString)
|
|
||||||
StringComparisonType.STARTS_WITH -> string.startsWith(filterString)
|
|
||||||
StringComparisonType.ENDS_WITH -> string.endsWith(filterString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is OpcodesFilter -> opcodes?.contains(instruction.opcode) == true
|
|
||||||
else -> throw IllegalStateException("Unknown InstructionFilter type: ${this::class.java}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters != null && !matchIndices(instructionsOrNull ?: return false, "match") {
|
|
||||||
filters.forEach { filter ->
|
filters.forEach { filter ->
|
||||||
|
val filterMatches: Instruction.() -> Boolean = { filter.matches(currentMethod, this) }
|
||||||
|
|
||||||
when (val location = filter.location) {
|
when (val location = filter.location) {
|
||||||
is MatchAfterImmediately -> after { filter.evaluate(this) }
|
is MatchAfterImmediately -> after { filterMatches() }
|
||||||
is MatchAfterWithin -> after(1..location.matchDistance) { filter.evaluate(this) }
|
is MatchAfterWithin -> after(1..location.matchDistance) { filterMatches() }
|
||||||
is MatchAfterAnywhere -> add { filter.evaluate(this) }
|
is MatchAfterAnywhere -> add { filterMatches() }
|
||||||
is MatchAfterAtLeast -> after(
|
is MatchAfterAtLeast -> after(location.minimumDistanceFromLastInstruction..Int.MAX_VALUE) { filterMatches() }
|
||||||
location.minimumDistanceFromLastInstruction..Int.MAX_VALUE
|
is MatchAfterRange -> after(location.minimumDistanceFromLastInstruction..location.maximumDistanceFromLastInstruction) { filterMatches() }
|
||||||
) { filter.evaluate(this) }
|
is MatchFirst -> head { filterMatches() }
|
||||||
|
}
|
||||||
is MatchAfterRange -> after(
|
|
||||||
location.minimumDistanceFromLastInstruction..
|
|
||||||
location.maximumDistanceFromLastInstruction
|
|
||||||
) { filter.evaluate(this) }
|
|
||||||
|
|
||||||
is MatchFirst -> first { filter.evaluate(this) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val allStrings = buildList {
|
val allStrings = buildList {
|
||||||
@@ -183,30 +141,28 @@ class Fingerprint internal constructor(
|
|||||||
)
|
)
|
||||||
}.map { it.stringValue } + (strings ?: emptyList())
|
}.map { it.stringValue } + (strings ?: emptyList())
|
||||||
|
|
||||||
val method = if (allStrings.isNotEmpty())
|
val method = if (allStrings.isNotEmpty()) {
|
||||||
context.firstMethodOrNull(strings = allStrings.toTypedArray()) { match() } ?:
|
context.firstMethodOrNull(strings = allStrings.toTypedArray()) { match() }
|
||||||
// Maybe a better way exists
|
?: context(MatchContext()) { context.lookupMaps.methodsWithString.firstOrNull { it.match() } }
|
||||||
context(MatchContext()) {
|
} else {
|
||||||
context.lookupMaps.methodsWithString.first { it.match() }
|
context.firstMethodOrNull { match() }
|
||||||
}
|
} ?: return null
|
||||||
else context.firstMethodOrNull { match() } ?: return null
|
|
||||||
|
|
||||||
val instructionMatches = filters?.withIndex()?.map { (i, filter) ->
|
val instructionMatches = filters?.withIndex()?.map { (i, filter) ->
|
||||||
Match.InstructionMatch(filter, matchIndices.indices[i], method.getInstruction(i))
|
val matchIndex = matchIndices.indices[i]
|
||||||
|
|
||||||
|
Match.InstructionMatch(filter, matchIndex, method.getInstruction(matchIndex))
|
||||||
}
|
}
|
||||||
|
|
||||||
val stringMatches = if (strings != null) matchStrings.indices.map { i ->
|
_matchOrNull = Match(
|
||||||
// TODO: Should we use the methods string or the fingerprints string
|
|
||||||
Match.StringMatch(method.getInstruction(i).stringReference!!.string, i)
|
|
||||||
} else null
|
|
||||||
|
|
||||||
return Match(
|
|
||||||
context,
|
context,
|
||||||
context.lookupMaps.classDefsByType[method.definingClass]!!,
|
context.lookupMaps.classDefsByType[method.definingClass]!!,
|
||||||
method,
|
method,
|
||||||
instructionMatches,
|
instructionMatches,
|
||||||
stringMatches,
|
stringMatches,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return _matchOrNull
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,44 +221,37 @@ class Fingerprint internal constructor(
|
|||||||
): Match? {
|
): Match? {
|
||||||
if (_matchOrNull != null) return _matchOrNull
|
if (_matchOrNull != null) return _matchOrNull
|
||||||
|
|
||||||
if (returnType != null && !method.returnType.startsWith(returnType)) {
|
var stringMatches: List<Match.StringMatch>? = null
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accessFlags != null && accessFlags != method.accessFlags) {
|
val matchIndices = indexedMatcher<Instruction>()
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: parseParameters()
|
context(_: MatchContext)
|
||||||
if (parameters != null && !parametersStartsWith(method.parameterTypes, parameters)) {
|
fun Method.match(): Boolean {
|
||||||
return null
|
if (this@Fingerprint.accessFlags != null && this@Fingerprint.accessFlags != accessFlags)
|
||||||
}
|
return false
|
||||||
|
|
||||||
if (custom != null && !custom.invoke(method, classDef)) {
|
if (this@Fingerprint.returnType != null && !returnType.startsWith(this@Fingerprint.returnType))
|
||||||
return null
|
return false
|
||||||
}
|
|
||||||
|
|
||||||
// Legacy string declarations.
|
if (this@Fingerprint.parameters != null && !parametersStartsWith(
|
||||||
val stringMatches: List<Match.StringMatch>? = if (strings == null) {
|
parameterTypes,
|
||||||
null
|
this@Fingerprint.parameters
|
||||||
} else {
|
)
|
||||||
buildList {
|
)
|
||||||
val instructions = method.instructionsOrNull ?: return null
|
return false
|
||||||
|
|
||||||
|
if (custom != null && !custom(this, classDef))
|
||||||
|
return false
|
||||||
|
|
||||||
|
stringMatches = if (strings != null) {
|
||||||
|
val instructions = instructionsOrNull ?: return false
|
||||||
var stringsList: MutableList<String>? = null
|
var stringsList: MutableList<String>? = null
|
||||||
|
|
||||||
|
buildList {
|
||||||
instructions.forEachIndexed { instructionIndex, instruction ->
|
instructions.forEachIndexed { instructionIndex, instruction ->
|
||||||
if (
|
if (stringsList == null) stringsList = strings.toMutableList()
|
||||||
instruction.opcode != Opcode.CONST_STRING &&
|
|
||||||
instruction.opcode != Opcode.CONST_STRING_JUMBO
|
|
||||||
) {
|
|
||||||
return@forEachIndexed
|
|
||||||
}
|
|
||||||
|
|
||||||
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
val string = instruction.stringReference?.string ?: return@forEachIndexed
|
||||||
if (stringsList == null) {
|
|
||||||
stringsList = strings.toMutableList()
|
|
||||||
}
|
|
||||||
val index = stringsList.indexOfFirst(string::contains)
|
val index = stringsList.indexOfFirst(string::contains)
|
||||||
if (index < 0) return@forEachIndexed
|
if (index < 0) return@forEachIndexed
|
||||||
|
|
||||||
@@ -310,79 +259,36 @@ class Fingerprint internal constructor(
|
|||||||
stringsList.removeAt(index)
|
stringsList.removeAt(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stringsList == null || stringsList.isNotEmpty()) return null
|
if (stringsList == null || stringsList.isNotEmpty()) return false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val instructionMatches = if (filters == null) {
|
} else null
|
||||||
null
|
|
||||||
} else {
|
|
||||||
val instructions = method.instructionsOrNull?.toList() ?: return null
|
|
||||||
|
|
||||||
fun matchFilters(): List<Match.InstructionMatch>? {
|
return filters == null || matchIndices.apply {
|
||||||
val lastMethodIndex = instructions.lastIndex
|
filters.forEach { filter ->
|
||||||
var instructionMatches: MutableList<Match.InstructionMatch>? = null
|
val filterMatches: Instruction.() -> Boolean = { filter.matches(method, this) }
|
||||||
|
|
||||||
var firstInstructionIndex = 0
|
when (val location = filter.location) {
|
||||||
var lastMatchIndex = -1
|
is MatchAfterImmediately -> after { filterMatches() }
|
||||||
|
is MatchAfterWithin -> after(1..location.matchDistance) { filterMatches() }
|
||||||
firstFilterLoop@ while (true) {
|
is MatchAfterAnywhere -> add { filterMatches() }
|
||||||
// Matched index of the first filter.
|
is MatchAfterAtLeast -> after(location.minimumDistanceFromLastInstruction..Int.MAX_VALUE) { filterMatches() }
|
||||||
var firstFilterIndex = -1
|
is MatchAfterRange -> after(location.minimumDistanceFromLastInstruction..location.maximumDistanceFromLastInstruction) { filterMatches() }
|
||||||
var subIndex = firstInstructionIndex
|
is MatchFirst -> head { filterMatches() }
|
||||||
|
|
||||||
for (filterIndex in filters.indices) {
|
|
||||||
val filter = filters[filterIndex]
|
|
||||||
val location = filter.location
|
|
||||||
var instructionsMatched = false
|
|
||||||
|
|
||||||
while (subIndex <= lastMethodIndex &&
|
|
||||||
location.indexIsValidForMatching(
|
|
||||||
lastMatchIndex, subIndex
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
val instruction = instructions[subIndex]
|
|
||||||
if (filter.matches(method, instruction)) {
|
|
||||||
lastMatchIndex = subIndex
|
|
||||||
|
|
||||||
if (filterIndex == 0) {
|
|
||||||
firstFilterIndex = subIndex
|
|
||||||
}
|
|
||||||
if (instructionMatches == null) {
|
|
||||||
instructionMatches = ArrayList<Match.InstructionMatch>(filters.size)
|
|
||||||
}
|
|
||||||
instructionMatches += Match.InstructionMatch(filter, subIndex, instruction)
|
|
||||||
instructionsMatched = true
|
|
||||||
subIndex++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
subIndex++
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!instructionsMatched) {
|
|
||||||
if (filterIndex == 0) {
|
|
||||||
return null // First filter has no more matches to start from.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try again with the first filter, starting from
|
|
||||||
// the next possible first filter index.
|
|
||||||
firstInstructionIndex = firstFilterIndex + 1
|
|
||||||
instructionMatches?.clear()
|
|
||||||
continue@firstFilterLoop
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}(instructionsOrNull ?: return false)
|
||||||
// All instruction filters matches.
|
|
||||||
return instructionMatches
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
matchFilters() ?: return null
|
if (!context(MatchContext()) { method.match() }) return null
|
||||||
|
val instructionMatches = filters?.withIndex()?.map { (i, filter) ->
|
||||||
|
val matchIndex = matchIndices.indices[i]
|
||||||
|
Match.InstructionMatch(filter, matchIndex, method.getInstruction(matchIndex))
|
||||||
}
|
}
|
||||||
|
|
||||||
_matchOrNull = Match(
|
_matchOrNull = Match(
|
||||||
context,
|
context,
|
||||||
classDef,
|
context.lookupMaps.classDefsByType[method.definingClass]!!,
|
||||||
method,
|
method,
|
||||||
instructionMatches,
|
instructionMatches,
|
||||||
stringMatches,
|
stringMatches,
|
||||||
@@ -600,11 +506,7 @@ class Match internal constructor(
|
|||||||
* Accessing this property allocates a new mutable instance.
|
* Accessing this property allocates a new mutable instance.
|
||||||
* Use [originalClassDef] if mutable access is not required.
|
* Use [originalClassDef] if mutable access is not required.
|
||||||
*/
|
*/
|
||||||
val classDef by lazy {
|
val classDef by lazy { with(context) { originalClassDef.mutable() } }
|
||||||
with(context) {
|
|
||||||
originalClassDef.mutable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mutable version of [originalMethod].
|
* The mutable version of [originalMethod].
|
||||||
|
|||||||
@@ -251,52 +251,101 @@ class IndexedMatcher<T>() : Matcher<T, T.(lastMatchedIndex: Int, currentIndex: I
|
|||||||
|
|
||||||
private var lastMatchedIndex = -1
|
private var lastMatchedIndex = -1
|
||||||
private var currentIndex = -1
|
private var currentIndex = -1
|
||||||
// TODO: Hint to stop searching for performance: private var stop = false
|
|
||||||
// Also make the APIs advance indices (e.g. atLeast, atMost) for performance.
|
private var nextIndex: Int? = null
|
||||||
|
|
||||||
override fun invoke(haystack: Iterable<T>): Boolean {
|
override fun invoke(haystack: Iterable<T>): Boolean {
|
||||||
// Normalize to list
|
// Normalize to list
|
||||||
val hayList = haystack as? List<T> ?: haystack.toList()
|
val hay = haystack as? List<T> ?: haystack.toList()
|
||||||
|
|
||||||
_indices.clear()
|
_indices.clear()
|
||||||
lastMatchedIndex = -1
|
this@IndexedMatcher.lastMatchedIndex = -1
|
||||||
currentIndex = -1
|
currentIndex = -1
|
||||||
|
|
||||||
for (predicate in this) {
|
data class Frame(
|
||||||
var matched = false
|
val patternIndex: Int,
|
||||||
|
val lastMatchedIndex: Int,
|
||||||
|
val previousFrame: Frame?,
|
||||||
|
var nextHayIndex: Int,
|
||||||
|
val matchedIndex: Int
|
||||||
|
)
|
||||||
|
|
||||||
// Continue scanning from the position after the last successful match
|
val stack = ArrayDeque<Frame>()
|
||||||
for (i in (lastMatchedIndex + 1) until hayList.size) {
|
stack.add(
|
||||||
|
Frame(
|
||||||
|
patternIndex = 0,
|
||||||
|
lastMatchedIndex = -1,
|
||||||
|
previousFrame = null,
|
||||||
|
nextHayIndex = 0,
|
||||||
|
matchedIndex = -1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
while (stack.isNotEmpty()) {
|
||||||
|
val frame = stack.last()
|
||||||
|
|
||||||
|
if (frame.nextHayIndex >= hay.size || nextIndex == -1) {
|
||||||
|
stack.removeLast()
|
||||||
|
nextIndex = null
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val i = frame.nextHayIndex
|
||||||
currentIndex = i
|
currentIndex = i
|
||||||
val element = hayList[i]
|
lastMatchedIndex = frame.lastMatchedIndex
|
||||||
|
nextIndex = null
|
||||||
|
|
||||||
if (element.predicate(lastMatchedIndex, currentIndex)) {
|
if (this[frame.patternIndex](hay[i], lastMatchedIndex, currentIndex)) {
|
||||||
_indices += i
|
Frame(
|
||||||
lastMatchedIndex = i
|
patternIndex = frame.patternIndex + 1,
|
||||||
matched = true
|
lastMatchedIndex = i,
|
||||||
break
|
previousFrame = frame,
|
||||||
}
|
nextHayIndex = i + 1,
|
||||||
}
|
matchedIndex = i
|
||||||
|
).also {
|
||||||
if (!matched) {
|
if (it.patternIndex == size) {
|
||||||
return false
|
_indices += buildList(size) {
|
||||||
}
|
var f: Frame? = it
|
||||||
|
while (f != null && f.matchedIndex != -1) {
|
||||||
|
add(f.matchedIndex)
|
||||||
|
f = f.previousFrame
|
||||||
}
|
}
|
||||||
|
}.asReversed()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
}.let(stack::add)
|
||||||
|
}
|
||||||
|
|
||||||
fun first(predicate: T.(lastMatchedIndex: Int, currentIndex: Int) -> Boolean) =
|
frame.nextHayIndex = when (val nextIndex = nextIndex) {
|
||||||
|
null -> frame.nextHayIndex + 1
|
||||||
|
-1 -> 0 // Frame will be removed next loop.
|
||||||
|
else -> nextIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun head(predicate: T.(lastMatchedIndex: Int, currentIndex: Int) -> Boolean) =
|
||||||
add { lastMatchedIndex, currentIndex ->
|
add { lastMatchedIndex, currentIndex ->
|
||||||
currentIndex == 0 && predicate(lastMatchedIndex, currentIndex)
|
currentIndex == 0 && predicate(lastMatchedIndex, currentIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun first(predicate: T.() -> Boolean) =
|
fun head(predicate: T.() -> Boolean) =
|
||||||
first { _, _ -> predicate() }
|
head { _, _ -> predicate() }
|
||||||
|
|
||||||
fun after(range: IntRange = 1..1, predicate: T.(lastMatchedIndex: Int, currentIndex: Int) -> Boolean) =
|
fun after(range: IntRange = 1..1, predicate: T.(lastMatchedIndex: Int, currentIndex: Int) -> Boolean) =
|
||||||
add { lastMatchedIndex, currentIndex ->
|
add { lastMatchedIndex, currentIndex ->
|
||||||
currentIndex - lastMatchedIndex in range && predicate(lastMatchedIndex, currentIndex)
|
val distance = currentIndex - lastMatchedIndex
|
||||||
|
|
||||||
|
nextIndex = when {
|
||||||
|
distance < range.first -> lastMatchedIndex + range.first
|
||||||
|
distance > range.last -> -1
|
||||||
|
else -> return@add predicate(lastMatchedIndex, currentIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun after(range: IntRange = 1..1, predicate: T.() -> Boolean) =
|
fun after(range: IntRange = 1..1, predicate: T.() -> Boolean) =
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
|
|||||||
PatcherResult.PatchedDexFile(it.name, it.inputStream())
|
PatcherResult.PatchedDexFile(it.name, it.inputStream())
|
||||||
}.toSet()
|
}.toSet()
|
||||||
|
|
||||||
|
// Free up more memory, although it is unclear if this is actually helpful.
|
||||||
|
classDefs.clear()
|
||||||
System.gc()
|
System.gc()
|
||||||
|
|
||||||
return patchedDexFileResults
|
return patchedDexFileResults
|
||||||
@@ -163,7 +165,6 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi
|
|||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
try {
|
try {
|
||||||
classDefs.clear()
|
|
||||||
_lookupMaps = null
|
_lookupMaps = null
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
logger.warning("Failed to clear BytecodePatchContext: ${e.message}")
|
logger.warning("Failed to clear BytecodePatchContext: ${e.message}")
|
||||||
|
|||||||
@@ -170,14 +170,14 @@ internal object PatcherTest {
|
|||||||
val iterable = (1..10).toList()
|
val iterable = (1..10).toList()
|
||||||
val matcher = indexedMatcher<Int>()
|
val matcher = indexedMatcher<Int>()
|
||||||
|
|
||||||
matcher.apply { first { this > 5 } }
|
matcher.apply { head { this > 5 } }
|
||||||
assertFalse(
|
assertFalse(
|
||||||
matcher(iterable),
|
matcher(iterable),
|
||||||
"Should not match at any other index than first"
|
"Should not match at any other index than first"
|
||||||
)
|
)
|
||||||
matcher.clear()
|
matcher.clear()
|
||||||
|
|
||||||
matcher.apply { first { this == 1 } }(iterable)
|
matcher.apply { head { this == 1 } }(iterable)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
listOf(0),
|
listOf(0),
|
||||||
matcher.indices,
|
matcher.indices,
|
||||||
@@ -198,7 +198,7 @@ internal object PatcherTest {
|
|||||||
matcher.clear()
|
matcher.clear()
|
||||||
|
|
||||||
matcher.apply {
|
matcher.apply {
|
||||||
first { this == 1 }
|
head { this == 1 }
|
||||||
add { this == 2 }
|
add { this == 2 }
|
||||||
add { this == 4 }
|
add { this == 4 }
|
||||||
}(iterable)
|
}(iterable)
|
||||||
@@ -238,8 +238,8 @@ internal object PatcherTest {
|
|||||||
matcher.clear()
|
matcher.clear()
|
||||||
|
|
||||||
matcher.apply {
|
matcher.apply {
|
||||||
first { this == 1 }
|
head { this == 1 }
|
||||||
after(2..5) { this == 4}
|
after(2..5) { this == 4 }
|
||||||
add { this == 8 }
|
add { this == 8 }
|
||||||
add { this == 9 }
|
add { this == 9 }
|
||||||
}(iterable)
|
}(iterable)
|
||||||
@@ -299,7 +299,7 @@ internal object PatcherTest {
|
|||||||
val method by gettingFirstMethod {
|
val method by gettingFirstMethod {
|
||||||
implementation {
|
implementation {
|
||||||
matchIndices(instructions, "match") {
|
matchIndices(instructions, "match") {
|
||||||
first { opcode == Opcode.CONST_STRING }
|
head { opcode == Opcode.CONST_STRING }
|
||||||
add { opcode == Opcode.IPUT_OBJECT }
|
add { opcode == Opcode.IPUT_OBJECT }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user