mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-11 12:04:39 +02:00
feat(YouTube): support version 19.02.39
This commit is contained in:
parent
5e12e0fa6a
commit
b83a1bdd45
@ -1,19 +0,0 @@
|
||||
package app.revanced.patches.shared.fingerprints.litho
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
object IdentifierFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
opcodes = listOf(
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE_RANGE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.CONST_STRING
|
||||
),
|
||||
strings = listOf("Element missing type extension")
|
||||
)
|
@ -4,11 +4,13 @@ import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.shared.fingerprints.litho.EmptyComponentBuilderFingerprint
|
||||
import app.revanced.patches.shared.fingerprints.litho.IdentifierFingerprint
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getEmptyStringInstructionIndex
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
@ -17,10 +19,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
object ComponentParserPatch : BytecodePatch(
|
||||
setOf(
|
||||
EmptyComponentBuilderFingerprint,
|
||||
IdentifierFingerprint
|
||||
)
|
||||
setOf(EmptyComponentBuilderFingerprint)
|
||||
) {
|
||||
private lateinit var emptyComponentLabel: String
|
||||
internal lateinit var insertMethod: MutableMethod
|
||||
@ -60,15 +59,28 @@ object ComponentParserPatch : BytecodePatch(
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableMethod.getTargetIndexDownTo(
|
||||
startIndex: Int,
|
||||
opcode: Opcode
|
||||
): Int {
|
||||
for (index in startIndex downTo 0) {
|
||||
if (getInstruction(index).opcode != opcode)
|
||||
continue
|
||||
|
||||
return index
|
||||
}
|
||||
throw PatchException("Failed to find hook method")
|
||||
}
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
|
||||
/**
|
||||
* Shared fingerprint
|
||||
*/
|
||||
EmptyComponentBuilderFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
EmptyComponentBuilderFingerprint.result?.let { result ->
|
||||
result.mutableMethod.apply {
|
||||
insertMethod = this
|
||||
emptyComponentIndex = it.scanResult.patternScanResult!!.startIndex + 1
|
||||
emptyComponentIndex = result.scanResult.patternScanResult!!.startIndex + 1
|
||||
|
||||
val builderMethodDescriptor =
|
||||
getInstruction<ReferenceInstruction>(emptyComponentIndex).reference
|
||||
@ -94,22 +106,19 @@ object ComponentParserPatch : BytecodePatch(
|
||||
getInstruction<TwoRegisterInstruction>(stringBuilderIndex).registerA
|
||||
|
||||
insertIndex = stringBuilderIndex + 1
|
||||
}
|
||||
} ?: throw EmptyComponentBuilderFingerprint.exception
|
||||
|
||||
/**
|
||||
* Only used in YouTube
|
||||
*/
|
||||
IdentifierFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val identifierIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
val objectIndex = it.scanResult.patternScanResult!!.endIndex + 1
|
||||
|
||||
val emptyStringIndex = getEmptyStringInstructionIndex()
|
||||
val identifierIndex = getTargetIndexDownTo(emptyStringIndex, Opcode.IPUT_OBJECT)
|
||||
identifierRegister =
|
||||
getInstruction<OneRegisterInstruction>(identifierIndex).registerA
|
||||
getInstruction<TwoRegisterInstruction>(identifierIndex).registerA
|
||||
|
||||
val objectIndex = implementation!!.instructions.let {
|
||||
emptyStringIndex + it.subList(emptyStringIndex, it.size - 1).indexOfFirst { instruction ->
|
||||
instruction.opcode == Opcode.INVOKE_VIRTUAL
|
||||
}
|
||||
}
|
||||
objectRegister = getInstruction<BuilderInstruction35c>(objectIndex).registerC
|
||||
}
|
||||
}
|
||||
|
||||
} ?: throw EmptyComponentBuilderFingerprint.exception
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package app.revanced.patches.shared.patch.transformation
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.util.findMutableMethodOf
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch(emptySet()) {
|
||||
abstract fun filterMap(
|
||||
classDef: ClassDef,
|
||||
method: Method,
|
||||
instruction: Instruction,
|
||||
instructionIndex: Int
|
||||
): T?
|
||||
|
||||
abstract fun transform(mutableMethod: MutableMethod, entry: T)
|
||||
|
||||
// Returns the patch indices as a Sequence, which will execute lazily.
|
||||
fun findPatchIndices(classDef: ClassDef, method: Method): Sequence<T>? {
|
||||
return method.implementation?.instructions?.asSequence()?.withIndex()?.mapNotNull { (index, instruction) ->
|
||||
filterMap(classDef, method, instruction, index)
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// Find all methods to patch
|
||||
buildMap {
|
||||
context.classes.forEach { classDef ->
|
||||
val methods = buildList {
|
||||
classDef.methods.forEach { method ->
|
||||
// Since the Sequence executes lazily,
|
||||
// using any() results in only calling
|
||||
// filterMap until the first index has been found.
|
||||
if (findPatchIndices(classDef, method)?.any() == true) add(method)
|
||||
}
|
||||
}
|
||||
|
||||
if (methods.isNotEmpty()) {
|
||||
put(classDef, methods)
|
||||
}
|
||||
}
|
||||
}.forEach { (classDef, methods) ->
|
||||
// And finally transform the methods...
|
||||
val mutableClass = context.proxy(classDef).mutableClass
|
||||
|
||||
methods.map(mutableClass::findMutableMethodOf).forEach methods@{ mutableMethod ->
|
||||
val patchIndices = findPatchIndices(mutableClass, mutableMethod)?.toCollection(ArrayDeque())
|
||||
?: return@methods
|
||||
|
||||
while (!patchIndices.isEmpty()) transform(mutableMethod, patchIndices.removeLast())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
|
||||
@Patch(
|
||||
name = "Remove viewer discretion dialog",
|
||||
description = "Removes the dialog that appears when you try to watch a video that has been age-restricted " +
|
||||
description = "Adds an option to remove the dialog that appears when opening a video that has been age-restricted " +
|
||||
"by accepting it automatically. This does not bypass the age restriction.",
|
||||
dependencies = [SettingsPatch::class],
|
||||
compatiblePackages = [
|
||||
|
@ -112,4 +112,4 @@ object AmbientModeSwitchPatch : BytecodePatch(
|
||||
SettingsPatch.updatePatchStatus("Ambient mode switch")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -2,18 +2,18 @@ package app.revanced.patches.youtube.misc.externalbrowser
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.youtube.misc.externalbrowser.fingerprints.ExternalBrowserPrimaryFingerprint
|
||||
import app.revanced.patches.youtube.misc.externalbrowser.fingerprints.ExternalBrowserSecondaryFingerprint
|
||||
import app.revanced.patches.youtube.misc.externalbrowser.fingerprints.ExternalBrowserTertiaryFingerprint
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.shared.patch.transformation.AbstractTransformInstructionsPatch
|
||||
import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getStringInstructionIndex
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
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.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
|
||||
@Patch(
|
||||
name = "Enable external browser",
|
||||
@ -47,35 +47,36 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object ExternalBrowserPatch : BytecodePatch(
|
||||
setOf(
|
||||
ExternalBrowserPrimaryFingerprint,
|
||||
ExternalBrowserSecondaryFingerprint,
|
||||
ExternalBrowserTertiaryFingerprint
|
||||
)
|
||||
object ExternalBrowserPatch : AbstractTransformInstructionsPatch<Pair<Int, Int>>(
|
||||
) {
|
||||
override fun filterMap(
|
||||
classDef: ClassDef,
|
||||
method: Method,
|
||||
instruction: Instruction,
|
||||
instructionIndex: Int
|
||||
): Pair<Int, Int>? {
|
||||
if (instruction !is ReferenceInstruction) return null
|
||||
val reference = instruction.reference as? StringReference ?: return null
|
||||
|
||||
if (reference.string != "android.support.customtabs.action.CustomTabsService") return null
|
||||
|
||||
return instructionIndex to (instruction as OneRegisterInstruction).registerA
|
||||
}
|
||||
|
||||
override fun transform(mutableMethod: MutableMethod, entry: Pair<Int, Int>) {
|
||||
val (intentStringIndex, register) = entry
|
||||
|
||||
// Hook the intent string.
|
||||
mutableMethod.addInstructions(
|
||||
intentStringIndex + 1, """
|
||||
invoke-static {v$register}, $MISC_PATH/ExternalBrowserPatch;->enableExternalBrowser(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
|
||||
arrayOf(
|
||||
ExternalBrowserPrimaryFingerprint,
|
||||
ExternalBrowserSecondaryFingerprint,
|
||||
ExternalBrowserTertiaryFingerprint
|
||||
).forEach { fingerprint ->
|
||||
fingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val targetIndex =
|
||||
getStringInstructionIndex("android.support.customtabs.action.CustomTabsService")
|
||||
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
targetIndex + 1, """
|
||||
invoke-static {v$register}, $MISC_PATH/ExternalBrowserPatch;->enableExternalBrowser(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw fingerprint.exception
|
||||
}
|
||||
super.execute(context)
|
||||
|
||||
/**
|
||||
* Add settings
|
||||
@ -87,6 +88,5 @@ object ExternalBrowserPatch : BytecodePatch(
|
||||
)
|
||||
|
||||
SettingsPatch.updatePatchStatus("Enable external browser")
|
||||
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.externalbrowser.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
object ExternalBrowserPrimaryFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
opcodes = listOf(
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.CONST_STRING
|
||||
),
|
||||
strings = listOf("android.support.customtabs.action.CustomTabsService")
|
||||
)
|
@ -1,11 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.externalbrowser.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object ExternalBrowserSecondaryFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
strings = listOf("android.support.customtabs.action.CustomTabsService")
|
||||
)
|
@ -1,18 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.externalbrowser.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
object ExternalBrowserTertiaryFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
opcodes = listOf(
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.CONST_STRING
|
||||
),
|
||||
strings = listOf("android.support.customtabs.action.CustomTabsService")
|
||||
)
|
@ -54,18 +54,18 @@ object LanguageSelectorPatch : BytecodePatch(
|
||||
override fun execute(context: BytecodeContext) {
|
||||
|
||||
val result = GeneralPrefsFingerprint.result // YouTube v18.33.xx ~
|
||||
?: GeneralPrefsLegacyFingerprint.result // ~ YouTube v18.33.xx
|
||||
?: GeneralPrefsLegacyFingerprint.result // ~ YouTube v18.32.xx
|
||||
?: throw GeneralPrefsFingerprint.exception
|
||||
|
||||
result.let {
|
||||
it.mutableMethod.apply {
|
||||
val targetIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
val insertIndex = it.scanResult.patternScanResult!!.endIndex - 2
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
targetIndex, """
|
||||
insertIndex, """
|
||||
invoke-static {}, $MISC_PATH/LanguageSelectorPatch;->enableLanguageSwitch()Z
|
||||
move-result v$targetRegister
|
||||
move-result v$insertRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
@ -3,15 +3,18 @@ package app.revanced.patches.youtube.misc.language.fingerprints
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* Compatible with YouTube v18.33.40~
|
||||
*/
|
||||
object GeneralPrefsFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
parameters = emptyList(),
|
||||
opcodes = listOf(
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.SGET,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT
|
||||
),
|
||||
strings = listOf("bedtime_reminder_toggle"),
|
||||
customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("/GeneralPrefsFragment;") }
|
||||
|
@ -3,6 +3,9 @@ package app.revanced.patches.youtube.misc.language.fingerprints
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* Compatible with ~YouTube v18.32.39
|
||||
*/
|
||||
object GeneralPrefsLegacyFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
parameters = emptyList(),
|
||||
@ -12,7 +15,9 @@ object GeneralPrefsLegacyFingerprint : MethodFingerprint(
|
||||
Opcode.CONST_4,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_NEZ
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT
|
||||
),
|
||||
strings = listOf("bedtime_reminder_toggle"),
|
||||
customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("/GeneralPrefsFragment;") }
|
||||
|
@ -19,8 +19,8 @@ import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@Patch(
|
||||
@ -83,8 +83,8 @@ object SeekMessagePatch : BytecodePatch(
|
||||
/**
|
||||
* Added in YouTube v18.29.xx~
|
||||
*/
|
||||
SeekEduUndoOverlayFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
SeekEduUndoOverlayFingerprint.result?.let { result ->
|
||||
result.mutableMethod.apply {
|
||||
val seekUndoCalls = implementation!!.instructions.withIndex()
|
||||
.filter { instruction ->
|
||||
(instruction.value as? WideLiteralInstruction)?.wideLiteral == SeekUndoEduOverlayStub
|
||||
@ -92,38 +92,31 @@ object SeekMessagePatch : BytecodePatch(
|
||||
val insertIndex = seekUndoCalls.elementAt(seekUndoCalls.size - 1).index
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
for (index in insertIndex until implementation!!.instructions.size) {
|
||||
val targetInstruction = getInstruction(index)
|
||||
if (targetInstruction.opcode != Opcode.INVOKE_VIRTUAL)
|
||||
continue
|
||||
|
||||
if (((targetInstruction as Instruction35c).reference as MethodReference).name != "setOnClickListener")
|
||||
continue
|
||||
|
||||
// Force close occurs only in YouTube v18.36.xx unless we add this.
|
||||
if (SettingsPatch.is1836)
|
||||
addComponent(insertIndex, index - 1)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex, fixComponent + """
|
||||
invoke-static {}, $PLAYER->hideSeekUndoMessage()Z
|
||||
move-result v$insertRegister
|
||||
if-nez v$insertRegister, :default
|
||||
""", ExternalLabel("default", getInstruction(index + 1))
|
||||
)
|
||||
|
||||
/**
|
||||
* Add settings
|
||||
*/
|
||||
SettingsPatch.addPreference(
|
||||
arrayOf(
|
||||
"PREFERENCE: PLAYER_SETTINGS",
|
||||
"SETTINGS: HIDE_SEEK_UNDO_MESSAGE"
|
||||
)
|
||||
)
|
||||
|
||||
break
|
||||
val jumpIndex = implementation!!.instructions.let {
|
||||
insertIndex + it.subList(insertIndex, it.size - 1).indexOfFirst { instruction ->
|
||||
instruction.opcode == Opcode.INVOKE_VIRTUAL
|
||||
&& ((instruction as? ReferenceInstruction)?.reference as? MethodReference)?.name == "setOnClickListener"
|
||||
}
|
||||
}
|
||||
val constComponent = getConstComponent(insertIndex, jumpIndex - 1)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex, constComponent + """
|
||||
invoke-static {}, $PLAYER->hideSeekUndoMessage()Z
|
||||
move-result v$insertRegister
|
||||
if-nez v$insertRegister, :default
|
||||
""", ExternalLabel("default", getInstruction(jumpIndex + 1))
|
||||
)
|
||||
|
||||
/**
|
||||
* Add settings
|
||||
*/
|
||||
SettingsPatch.addPreference(
|
||||
arrayOf(
|
||||
"PREFERENCE: PLAYER_SETTINGS",
|
||||
"SETTINGS: HIDE_SEEK_UNDO_MESSAGE"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,30 +134,24 @@ object SeekMessagePatch : BytecodePatch(
|
||||
|
||||
}
|
||||
|
||||
private var fixComponent: String = ""
|
||||
|
||||
private fun MutableMethod.addComponent(
|
||||
private fun MutableMethod.getConstComponent(
|
||||
startIndex: Int,
|
||||
endIndex: Int
|
||||
) {
|
||||
val fixRegister =
|
||||
): String {
|
||||
val constRegister =
|
||||
getInstruction<FiveRegisterInstruction>(endIndex).registerE
|
||||
|
||||
for (index in endIndex downTo startIndex) {
|
||||
val opcode = getInstruction(index).opcode
|
||||
if (opcode != Opcode.CONST_16)
|
||||
if (getInstruction(index).opcode != Opcode.CONST_16)
|
||||
continue
|
||||
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
if (register != fixRegister)
|
||||
if (getInstruction<OneRegisterInstruction>(index).registerA != constRegister)
|
||||
continue
|
||||
|
||||
val fixValue = getInstruction<WideLiteralInstruction>(index).wideLiteral.toInt()
|
||||
val constValue = getInstruction<WideLiteralInstruction>(index).wideLiteral.toInt()
|
||||
|
||||
fixComponent = "const/16 v$fixRegister, $fixValue"
|
||||
|
||||
break
|
||||
return "const/16 v$constRegister, $constValue"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
package app.revanced.patches.youtube.seekbar.append
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.youtube.utils.fingerprints.TotalTimeFingerprint
|
||||
@ -13,11 +11,16 @@ import app.revanced.patches.youtube.utils.integrations.Constants.SEEKBAR
|
||||
import app.revanced.patches.youtube.utils.overridequality.OverrideQualityHookPatch
|
||||
import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch
|
||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
|
||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.TotalTime
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.getWideLiteralInstructionIndex
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@Patch(
|
||||
name = "Append time stamps information",
|
||||
@ -60,36 +63,28 @@ object AppendTimeStampInformationPatch : BytecodePatch(
|
||||
setOf(TotalTimeFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
TotalTimeFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
var setTextIndex = -1
|
||||
|
||||
for ((textViewIndex, textViewInstruction) in implementation!!.instructions.withIndex()) {
|
||||
if (textViewInstruction.opcode != Opcode.INVOKE_VIRTUAL) continue
|
||||
|
||||
if (getInstruction<ReferenceInstruction>(textViewIndex).reference.toString() ==
|
||||
"Landroid/widget/TextView;->getText()Ljava/lang/CharSequence;"
|
||||
) {
|
||||
setTextIndex = textViewIndex + 2
|
||||
val setTextRegister = getInstruction<Instruction35c>(setTextIndex).registerC
|
||||
val textViewRegister =
|
||||
getInstruction<Instruction35c>(textViewIndex).registerC
|
||||
|
||||
addInstructions(
|
||||
setTextIndex, """
|
||||
invoke-static {v$setTextRegister}, $SEEKBAR->appendTimeStampInformation(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$setTextRegister
|
||||
"""
|
||||
)
|
||||
addInstruction(
|
||||
textViewIndex,
|
||||
"invoke-static {v$textViewRegister}, $SEEKBAR->setContainerClickListener(Landroid/view/View;)V"
|
||||
)
|
||||
break
|
||||
TotalTimeFingerprint.result?.let { result ->
|
||||
result.mutableMethod.apply {
|
||||
val constIndex = getWideLiteralInstructionIndex(TotalTime)
|
||||
val charSequenceIndex = implementation!!.instructions.let {
|
||||
constIndex + it.subList(constIndex, it.size - 1).indexOfFirst { instruction ->
|
||||
instruction.opcode == Opcode.MOVE_RESULT_OBJECT
|
||||
}
|
||||
}
|
||||
if (setTextIndex == -1)
|
||||
throw PatchException("target Instruction not found!")
|
||||
val charSequenceRegister = getInstruction<OneRegisterInstruction>(charSequenceIndex).registerA
|
||||
val textViewIndex = indexOfFirstInstruction {
|
||||
getReference<MethodReference>()?.name == "getText"
|
||||
}
|
||||
val textViewRegister =
|
||||
getInstruction<Instruction35c>(textViewIndex).registerC
|
||||
|
||||
addInstructions(
|
||||
textViewIndex, """
|
||||
invoke-static {v$textViewRegister}, $SEEKBAR->setContainerClickListener(Landroid/view/View;)V
|
||||
invoke-static {v$charSequenceRegister}, $SEEKBAR->appendTimeStampInformation(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$charSequenceRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw TotalTimeFingerprint.exception
|
||||
|
||||
|
@ -11,7 +11,6 @@ import app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints.UserW
|
||||
import app.revanced.patches.youtube.utils.integrations.Constants.SHORTS
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getWideLiteralInstructionIndex
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
@ -53,7 +52,7 @@ object DisableShortsOnStartupPatch : BytecodePatch(
|
||||
|
||||
UserWasInShortsFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val insertIndex = getWideLiteralInstructionIndex(45381394)
|
||||
val insertIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructionsWithLabels(
|
||||
|
@ -1,13 +1,14 @@
|
||||
package app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.util.fingerprint.LiteralValueFingerprint
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
object UserWasInShortsFingerprint : LiteralValueFingerprint(
|
||||
object UserWasInShortsFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Ljava/lang/Object;"),
|
||||
strings = listOf("Failed to read user_was_in_shorts proto after successful warmup"),
|
||||
literalSupplier = { 45381394 }
|
||||
opcodes = listOf(Opcode.CONST_WIDE_32),
|
||||
strings = listOf("Failed to read user_was_in_shorts proto after successful warmup")
|
||||
)
|
@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.shared.patch.litho.ComponentParserPatch
|
||||
import app.revanced.patches.youtube.utils.browseid.fingerprints.BrowseIdClassFingerprint
|
||||
@ -13,6 +14,7 @@ import app.revanced.patches.youtube.utils.integrations.Constants.UTILS_PATH
|
||||
import app.revanced.patches.youtube.utils.litho.LithoFilterPatch
|
||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getStringInstructionIndex
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
@ -38,22 +40,29 @@ object BrowseIdHookPatch : BytecodePatch(
|
||||
* This class handles BrowseId.
|
||||
* Pass an instance of this class to integrations to use Java Reflection.
|
||||
*/
|
||||
BrowseIdClassFingerprint.result
|
||||
?.mutableClass?.methods?.find { method -> method.name == "<init>" }
|
||||
?.apply {
|
||||
val browseIdFieldIndex = implementation!!.instructions.indexOfFirst { instruction ->
|
||||
instruction.opcode == Opcode.IPUT_OBJECT
|
||||
}
|
||||
val browseIdFieldName =
|
||||
(getInstruction<ReferenceInstruction>(browseIdFieldIndex).reference as FieldReference).name
|
||||
BrowseIdClassFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val targetIndex = getStringInstructionIndex("VL") - 1
|
||||
val targetReference = getInstruction<ReferenceInstruction>(targetIndex).reference
|
||||
val targetClass = context.findClass((targetReference as FieldReference).definingClass)!!.mutableClass
|
||||
|
||||
addInstructions(
|
||||
1, """
|
||||
const-string v0, "$browseIdFieldName"
|
||||
invoke-static {p0, v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->initialize(Ljava/lang/Object;Ljava/lang/String;)V
|
||||
"""
|
||||
)
|
||||
} ?: throw BrowseIdClassFingerprint.exception
|
||||
targetClass.methods.find { method -> method.name == "<init>" }
|
||||
?.apply {
|
||||
val browseIdFieldIndex = implementation!!.instructions.indexOfFirst { instruction ->
|
||||
instruction.opcode == Opcode.IPUT_OBJECT
|
||||
}
|
||||
val browseIdFieldName =
|
||||
(getInstruction<ReferenceInstruction>(browseIdFieldIndex).reference as FieldReference).name
|
||||
|
||||
addInstructions(
|
||||
1, """
|
||||
const-string v0, "$browseIdFieldName"
|
||||
invoke-static {p0, v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->initialize(Ljava/lang/Object;Ljava/lang/String;)V
|
||||
"""
|
||||
)
|
||||
} ?: throw PatchException("BrowseIdClass not found!")
|
||||
}
|
||||
} ?: throw BrowseIdClassFingerprint.exception
|
||||
|
||||
/**
|
||||
* Set BrowseId to integrations.
|
||||
|
@ -1,7 +1,12 @@
|
||||
package app.revanced.patches.youtube.utils.browseid.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object BrowseIdClassFingerprint : MethodFingerprint(
|
||||
strings = listOf("\u0001\t\u0000\u0001\u0002\u0010\t\u0000\u0000\u0001\u0002\u1008\u0000\u0003\u1008\u0002\u0005\u1008\u0003\u0006\u1409\u0005\u0007\u1007\u0004\u0008\u1009\u0006\u000c\u1008\n\u000e\u180c\u000b\u0010\u1007\r")
|
||||
returnType = "Ljava/lang/Object;",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.SYNTHETIC,
|
||||
parameters = listOf("Ljava/lang/Object;", "L"),
|
||||
strings = listOf("VL")
|
||||
)
|
@ -26,6 +26,5 @@ object GeneralByteBufferFingerprint : MethodFingerprint(
|
||||
Opcode.IPUT,
|
||||
Opcode.IPUT,
|
||||
Opcode.GOTO
|
||||
),
|
||||
customFingerprint = { methodDef, _ -> methodDef.name == "f" }
|
||||
)
|
||||
)
|
@ -12,29 +12,63 @@ import java.io.Closeable
|
||||
object PlayerResponsePatch : BytecodePatch(
|
||||
setOf(PlayerParameterBuilderFingerprint)
|
||||
), Closeable, MutableSet<PlayerResponsePatch.Hook> by mutableSetOf() {
|
||||
private const val VIDEO_ID_PARAMETER = 1
|
||||
private const val PLAYER_PARAMETER = 3
|
||||
private const val IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11
|
||||
private var VIDEO_ID_PARAMETER = 1
|
||||
private var PLAYER_PARAMETER = 3
|
||||
private var IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11
|
||||
private var freeRegister = 0
|
||||
private var shouldApplyNewMethod = false
|
||||
|
||||
private lateinit var playerResponseMethod: MutableMethod
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
playerResponseMethod = PlayerParameterBuilderFingerprint.result?.mutableMethod
|
||||
?: throw PlayerParameterBuilderFingerprint.exception
|
||||
|
||||
playerResponseMethod.apply {
|
||||
freeRegister = implementation!!.registerCount - parameters.size - 2
|
||||
shouldApplyNewMethod = freeRegister > 2
|
||||
if (shouldApplyNewMethod) {
|
||||
IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = freeRegister
|
||||
PLAYER_PARAMETER = freeRegister - 1
|
||||
VIDEO_ID_PARAMETER = freeRegister - 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
fun hookVideoId(hook: Hook) = playerResponseMethod.addInstruction(
|
||||
0,
|
||||
"invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
|
||||
)
|
||||
fun hookVideoId(hook: Hook) {
|
||||
playerResponseMethod.apply {
|
||||
val instruction =
|
||||
if (shouldApplyNewMethod)
|
||||
"invoke-static {v$VIDEO_ID_PARAMETER, v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
|
||||
else
|
||||
"invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
|
||||
addInstruction(
|
||||
0,
|
||||
instruction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun hookPlayerParameter(hook: Hook) = playerResponseMethod.addInstructions(
|
||||
0, """
|
||||
invoke-static {p$VIDEO_ID_PARAMETER, p$PLAYER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
|
||||
move-result-object p$PLAYER_PARAMETER
|
||||
"""
|
||||
)
|
||||
fun hookPlayerParameter(hook: Hook) {
|
||||
playerResponseMethod.apply {
|
||||
val instruction =
|
||||
if (shouldApplyNewMethod)
|
||||
"""
|
||||
invoke-static {v$VIDEO_ID_PARAMETER, v$PLAYER_PARAMETER, v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
|
||||
move-result-object p3
|
||||
"""
|
||||
else
|
||||
"""
|
||||
invoke-static {p$VIDEO_ID_PARAMETER, p$PLAYER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
|
||||
move-result-object p$PLAYER_PARAMETER
|
||||
"""
|
||||
addInstructions(
|
||||
0,
|
||||
instruction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the order in order to preserve insertion order of the hooks.
|
||||
val beforeVideoIdHooks = filterIsInstance<Hook.PlayerBeforeVideoId>().asReversed()
|
||||
@ -45,6 +79,16 @@ object PlayerResponsePatch : BytecodePatch(
|
||||
afterVideoIdHooks.forEach(::hookPlayerParameter)
|
||||
videoIdHooks.forEach(::hookVideoId)
|
||||
beforeVideoIdHooks.forEach(::hookPlayerParameter)
|
||||
|
||||
if (shouldApplyNewMethod) {
|
||||
playerResponseMethod.addInstructions(
|
||||
0, """
|
||||
move-object v$VIDEO_ID_PARAMETER, p1
|
||||
move-object v$PLAYER_PARAMETER, p3
|
||||
move/from16 v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER, p11
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class Hook(private val methodDescriptor: String) {
|
||||
|
@ -22,9 +22,11 @@ import app.revanced.patches.youtube.utils.returnyoutubedislike.shorts.ReturnYouT
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
|
||||
@Patch(
|
||||
name = "Return YouTube Dislike",
|
||||
@ -100,14 +102,14 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
|
||||
it.mutableMethod.apply {
|
||||
val conversionContextFieldIndex = implementation!!.instructions.indexOfFirst { instruction ->
|
||||
instruction.opcode == Opcode.IGET_OBJECT
|
||||
&& (instruction as ReferenceInstruction).reference.toString().endsWith("Ljava/util/Map;")
|
||||
&& instruction.getReference<FieldReference>()?.type == "Ljava/util/Map;"
|
||||
} - 1
|
||||
val conversionContextFieldReference =
|
||||
getInstruction<ReferenceInstruction>(conversionContextFieldIndex).reference
|
||||
|
||||
val charSequenceIndex = implementation!!.instructions.indexOfFirst { instruction ->
|
||||
instruction.opcode == Opcode.IGET_OBJECT
|
||||
&& (instruction as ReferenceInstruction).reference.toString().endsWith("Ljava/util/BitSet;")
|
||||
&& instruction.getReference<FieldReference>()?.type == "Ljava/util/BitSet;"
|
||||
} - 1
|
||||
val charSequenceRegister = getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerA
|
||||
val freeRegister = getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerB
|
||||
|
@ -86,7 +86,6 @@ object SettingsPatch : AbstractSettingsResourcePatch(
|
||||
|
||||
val playServicesVersion = node.textContent.toInt()
|
||||
|
||||
is1836 = playServicesVersion in 233700000..233801999
|
||||
upward1828 = 232900000 <= playServicesVersion
|
||||
upward1831 = 233200000 <= playServicesVersion
|
||||
upward1834 = 233502000 <= playServicesVersion
|
||||
@ -156,7 +155,6 @@ object SettingsPatch : AbstractSettingsResourcePatch(
|
||||
private val threadPoolExecutor = Executors.newFixedThreadPool(THREAD_COUNT)
|
||||
|
||||
internal lateinit var contexts: ResourceContext
|
||||
internal var is1836: Boolean = false
|
||||
internal var upward1828: Boolean = false
|
||||
internal var upward1831: Boolean = false
|
||||
internal var upward1834: Boolean = false
|
||||
|
@ -10,31 +10,6 @@ object VideoIdWithoutShortsFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.DECLARED_SYNCHRONIZED,
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.MONITOR_ENTER,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.NEW_ARRAY,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.APUT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.CONST_4,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.MONITOR_EXIT,
|
||||
Opcode.RETURN_VOID,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.NEW_ARRAY,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.APUT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
@ -42,10 +17,7 @@ object VideoIdWithoutShortsFingerprint : MethodFingerprint(
|
||||
Opcode.MONITOR_EXIT,
|
||||
Opcode.RETURN_VOID,
|
||||
Opcode.MONITOR_EXIT,
|
||||
Opcode.RETURN_VOID,
|
||||
Opcode.MOVE_EXCEPTION,
|
||||
Opcode.MONITOR_EXIT,
|
||||
Opcode.THROW
|
||||
Opcode.RETURN_VOID
|
||||
),
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
methodDef.name == "l" && classDef.methods.count() == 17
|
||||
|
@ -87,6 +87,13 @@ fun Method.getWideLiteralInstructionIndex(literal: Long) = implementation?.let {
|
||||
}
|
||||
} ?: -1
|
||||
|
||||
fun Method.getEmptyStringInstructionIndex() = implementation?.let {
|
||||
it.instructions.indexOfFirst { instruction ->
|
||||
instruction.opcode == Opcode.CONST_STRING
|
||||
&& (instruction as? BuilderInstruction21c)?.reference.toString().isEmpty()
|
||||
}
|
||||
} ?: -1
|
||||
|
||||
fun Method.getStringInstructionIndex(value: String) = implementation?.let {
|
||||
it.instructions.indexOfFirst { instruction ->
|
||||
instruction.opcode == Opcode.CONST_STRING
|
||||
@ -138,6 +145,15 @@ inline fun <reified T : Reference> Instruction.getReference() =
|
||||
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) =
|
||||
this.implementation!!.instructions.indexOfFirst(predicate)
|
||||
|
||||
/**
|
||||
* Get the index of the last [Instruction] that matches the predicate.
|
||||
*
|
||||
* @param predicate The predicate to match.
|
||||
* @return The index of the first [Instruction] that matches the predicate.
|
||||
*/
|
||||
fun Method.indexOfLastInstruction(predicate: Instruction.() -> Boolean) =
|
||||
this.implementation!!.instructions.indexOfFirst(predicate)
|
||||
|
||||
/**
|
||||
* Return the resolved methods of [MethodFingerprint]s early.
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user