Skip to content

Commit

Permalink
fix: don't include all Litho patches, when not included
Browse files Browse the repository at this point in the history
  • Loading branch information
oSumAtrIX committed Jun 14, 2023
1 parent 1fe3e54 commit fc69491
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch.P
@HideAdsCompatibility
@Version("0.0.1")
class HideAdsResourcePatch : ResourcePatch {

override fun execute(context: ResourceContext): PatchResult {
PreferenceScreen.LAYOUT.addPreferences(
SwitchPreference(
Expand Down Expand Up @@ -200,6 +199,12 @@ class HideAdsResourcePatch : ResourcePatch {
StringResource("revanced_hide_mix_playlists_summary_on", "Mix playlists are hidden"),
StringResource("revanced_hide_mix_playlists_summary_off", "Mix playlists are shown")
),
SwitchPreference(
"revanced_hide_artist_cards",
StringResource("revanced_hide_artist_cards_title", "Hide artist cards"),
StringResource("revanced_hide_artist_cards_on", "Artist cards is hidden"),
StringResource("revanced_hide_artist_cards_off", "Artist cards is shown")
),
)

PreferenceScreen.ADS.addPreferences(
Expand Down Expand Up @@ -259,12 +264,17 @@ class HideAdsResourcePatch : ResourcePatch {
)
)

LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)

adAttributionId = ResourceMappingPatch.resourceMappings.single { it.name == "ad_attribution" }.id

return PatchResultSuccess()
}

internal companion object {
var adAttributionId: Long = -1

private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/patches/components/AdsFilter;"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ class HideButtonsPatch : ResourcePatch {
StringResource("revanced_hide_buttons_preference_screen_summary", "Hide or show buttons under videos")
)
)

LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)

return PatchResultSuccess()
}

private companion object {
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/patches/components/ButtonsFilter;"
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class HideShortsComponentsPatch : BytecodePatch(
)
) {
override fun execute(context: BytecodeContext): PatchResult {
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)

// region Hide the Shorts shelf.

ReelConstructorFingerprint.result?.let {
Expand All @@ -59,7 +61,7 @@ class HideShortsComponentsPatch : BytecodePatch(
injectHideViewCall(
insertIndex,
viewRegister,
CLASS_DESCRIPTOR,
FILTER_CLASS_DESCRIPTOR,
"hideShortsShelf"
)
}
Expand Down Expand Up @@ -89,7 +91,7 @@ class HideShortsComponentsPatch : BytecodePatch(
val viewRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(
checkCastIndex + 1,
"sput-object v$viewRegister, $CLASS_DESCRIPTOR->pivotBar:" +
"sput-object v$viewRegister, $FILTER_CLASS_DESCRIPTOR->pivotBar:" +
"Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;"
)
}
Expand All @@ -102,7 +104,7 @@ class HideShortsComponentsPatch : BytecodePatch(
throw RenderBottomNavigationBarFingerprint.toErrorResult()

RenderBottomNavigationBarFingerprint.result!!.mutableMethod.apply {
addInstruction(0, "invoke-static { }, $CLASS_DESCRIPTOR->hideNavigationBar()V")
addInstruction(0, "invoke-static { }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar()V")
}
} ?: return RenderBottomNavigationBarParentFingerprint.toErrorResult()

Expand All @@ -115,7 +117,7 @@ class HideShortsComponentsPatch : BytecodePatch(

addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, $CLASS_DESCRIPTOR->" +
"invoke-static { v$viewRegister }, $FILTER_CLASS_DESCRIPTOR->" +
"hideNavigationBar(Landroid/view/View;)Landroid/view/View;"
)
}
Expand All @@ -127,7 +129,7 @@ class HideShortsComponentsPatch : BytecodePatch(
}

private companion object {
private const val CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/components/ShortsFilter;"
private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/components/ShortsFilter;"

private enum class ShortsButtons(private val resourceName: String, private val methodName: String) {
COMMENTS("reel_dyn_comment", "hideShortsCommentsButton"),
Expand All @@ -139,7 +141,7 @@ class HideShortsComponentsPatch : BytecodePatch(

val setIdIndex = referencedIndex + 1
val viewRegister = method.getInstruction<FiveRegisterInstruction>(setIdIndex).registerC
method.injectHideViewCall(setIdIndex, viewRegister, CLASS_DESCRIPTOR, methodName)
method.injectHideViewCall(setIdIndex, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package app.revanced.patches.youtube.misc.litho.filter.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

object LithoFilterFingerprint : MethodFingerprint(
customFingerprint = custom@{ method, classDef ->
if (method.name != "<clinit>") return@custom false

classDef.type.endsWith("LithoFilterPatch;")
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import app.revanced.extensions.toErrorResult
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.BytecodePatch
Expand All @@ -15,46 +18,58 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.annotation.LithoFilterCompatibility
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ComponentContextParserFingerprint
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.EmptyComponentBuilderFingerprint
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ProtobufBufferFingerprint
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ReadComponentIdentifierFingerprint
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.*
import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
import java.io.Closeable

@DependsOn([IntegrationsPatch::class])
@Description("Hooks the method which parses the bytes into a ComponentContext to filter components.")
@LithoFilterCompatibility
@Version("0.0.1")
class LithoFilterPatch : BytecodePatch(
listOf(ComponentContextParserFingerprint)
) {
listOf(ComponentContextParserFingerprint, LithoFilterFingerprint)
), Closeable {
override fun execute(context: BytecodeContext): PatchResult {
ComponentContextParserFingerprint.result?.also {
arrayOf(
EmptyComponentBuilderFingerprint, ReadComponentIdentifierFingerprint, ProtobufBufferFingerprint
EmptyComponentBuilderFingerprint,
ReadComponentIdentifierFingerprint,
ProtobufBufferFingerprint
).forEach { fingerprint ->
if (!fingerprint.resolve(context, it.mutableMethod, it.mutableClass))
return fingerprint.toErrorResult()
if (fingerprint.resolve(context, it.mutableMethod, it.mutableClass)) return@forEach
return fingerprint.toErrorResult()
}
}?.let { result ->
val builderMethodIndex = EmptyComponentBuilderFingerprint.patternScanEndIndex
val emptyComponentFieldIndex = builderMethodIndex + 2

result.mutableMethod.apply {
val insertHookIndex = result.scanResult.patternScanResult!!.endIndex
val builderMethodDescriptor = getInstruction(builderMethodIndex).descriptor
val emptyComponentFieldDescriptor = getInstruction(emptyComponentFieldIndex).descriptor
// Register is overwritten right after it is used in this patch, therefore free to clobber.
val free = getInstruction<TwoRegisterInstruction>(insertHookIndex - 1).registerA
val free2 = getInstruction<OneRegisterInstruction>(insertHookIndex).registerA

// region Get free registers that this patch uses
// Registers are overwritten right after they are used in this patch, therefore free to clobber.

val freeRegistersInstruction = getInstruction<FiveRegisterInstruction>(insertHookIndex - 2)

// Later used to store the protobuf buffer object.
val free1 = getInstruction<OneRegisterInstruction>(insertHookIndex).registerA
// Later used to store the identifier of the component.
// This register currently holds a reference to the StringBuilder object
// that is required before clobbering.
val free2 = freeRegistersInstruction.registerC

@Suppress("UnnecessaryVariable")
// The register, this patch clobbers, is previously used for the StringBuilder,
// later on a new StringBuilder is instantiated on it.
val stringBuilderRegister = free
val stringBuilderRegister = free2

// endregion

// region Get references that this patch needs

val builderMethodDescriptor = getInstruction(builderMethodIndex).descriptor
val emptyComponentFieldDescriptor = getInstruction(emptyComponentFieldIndex).descriptor

val identifierRegister =
getInstruction<OneRegisterInstruction>(ReadComponentIdentifierFingerprint.patternScanEndIndex).registerA
Expand All @@ -69,57 +84,87 @@ class LithoFilterPatch : BytecodePatch(
getInstruction(ProtobufBufferFingerprint.patternScanEndIndex - 1).descriptor
val protobufBufferFieldDescriptor = "$protobufBufferRefTypeDescriptor->b:Ljava/nio/ByteBuffer;"

// endregion

// region Patch the method

// Insert the instructions that are responsible
// to return an EmptyComponent instead of the original component if the filter method returns false.
addInstructionsWithLabels(
insertHookIndex, // right after setting the component.pathBuilder field.
insertHookIndex,
"""
# Get the protobuf buffer object.
move-object/from16 v$free2, p$protobufParameterNumber
iget-object v$free2, v$free2, $protobufBufferRefTypeRefFieldDescriptor
check-cast v$free2, $protobufBufferRefTypeDescriptor
move-object/from16 v$free1, p$protobufParameterNumber
iget-object v$free1, v$free1, $protobufBufferRefTypeRefFieldDescriptor
check-cast v$free1, $protobufBufferRefTypeDescriptor
# Register "free" now holds the protobuf buffer object
iget-object v$free2, v$free2, $protobufBufferFieldDescriptor
iget-object v$free1, v$free1, $protobufBufferFieldDescriptor
# Invoke the filter method.
invoke-static { v$stringBuilderRegister, v$identifierRegister, v$free2 }, $FILTER_METHOD_DESCRIPTOR
move-result v$free
if-eqz v$free, :not_an_ad
invoke-static { v$stringBuilderRegister, v$identifierRegister, v$free1 }, $FILTER_METHOD_DESCRIPTOR
move-result v$free1
# If the filter method returned true, then return a replacement empty component.
move-object/from16 v$free, p1
invoke-static {v$free}, $builderMethodDescriptor
move-result-object v$free
iget-object v$free, v$free, $emptyComponentFieldDescriptor
return-object v$free
if-eqz v$free1, :unfiltered
move-object/from16 v$free2, p1
invoke-static {v$free2}, $builderMethodDescriptor
move-result-object v$free2
iget-object v$free2, v$free2, $emptyComponentFieldDescriptor
return-object v$free2
""",
ExternalLabel("not_an_ad", getInstruction(insertHookIndex))
// Used to jump over the instruction which block the component from being created.
ExternalLabel("unfiltered", getInstruction(insertHookIndex))
)
// endregion
}
} ?: return ComponentContextParserFingerprint.toErrorResult()

LithoFilterFingerprint.result?.mutableMethod?.apply {
removeInstructions(2, 4) // Remove dummy filter.

addFilter = { classDescriptor ->
addInstructions(
2,
"""
new-instance v1, $classDescriptor
invoke-direct {v1}, $classDescriptor-><init>()V
const/4 v2, ${filterCount++}
aput-object v1, v0, v2
"""
)
}
} ?: return LithoFilterFingerprint.toErrorResult()

return PatchResultSuccess()
}

private companion object {
override fun close() = LithoFilterFingerprint.result!!
.mutableMethod.replaceInstruction(0, "const/4 v0, $filterCount")

This comment has been minimized.

Copy link
@inotia00

inotia00 Jul 2, 2023

If the number of filters exceeds 8, an exception is raised
Because const/4 v0, 0x8 is an invalid value

This may not be an issue right now, but I think it would be better to use const/16 v0, $filterCount instead of const/4 v0, $filterCount

This comment has been minimized.

Copy link
@oSumAtrIX

oSumAtrIX Jul 2, 2023

Author Member

I have seen dexlib do similar conversions on const-string instructions automatically. Were you able to reproduce the crash? If so, please open a PR.

This comment has been minimized.

Copy link
@inotia00

inotia00 Jul 2, 2023

Even though all dependencies are set to the ReVanced repository rather than the local repository and built, the issue can be reproduced in certain situations

An error occurs under the following conditions:

  • When LithoFilterPatch.addFilter is called more than 8 times

The specific error log is as follows:

INFO: Reading dex files
INFO: Decoding AndroidManifest.xml only, because resources are not needed
INFO: Skipping change-package-name: Excluded by default
INFO: Skipping custom-video-buffer: Excluded by default
INFO: Skipping enable-android-debugging: Excluded by default
INFO: Skipping export-all-activities: Excluded by default
INFO: Skipping predictive-back-gesture: Excluded by default
INFO: Skipping remove-player-controls-background: Excluded by default
INFO: Skipping remove-screen-capture-restriction: Excluded by default
INFO: Skipping remove-screenshot-restriction: Excluded by default
INFO: Skipping spoof-wifi-connection: Excluded by default
INFO: Merging revanced-integrations-0.111.2-dev.1.apk
INFO: Deleting existing resource cache directory
INFO: Decoding resources
INFO: Executing patches
INFO: always-autorepeat succeeded
INFO: client-spoof succeeded
...
INFO: video-speed succeeded
INFO: wide-searchbar succeeded
[6,8] Invalid literal value: 8. Must be between -8 and 7, inclusive.
[4,4] A non-abstract/non-native method must have at least 1 instruction
SEVERE: Failed to close 'LithoFilterPatch': java.util.NoSuchElementException: Collection is empty.
        at kotlin.collections.CollectionsKt___CollectionsKt.first(_Collections.kt:201)
        at app.revanced.patcher.util.smali.InlineSmaliCompiler$Companion.compile(InlineSmaliCompiler.kt:58)
        at app.revanced.patcher.util.smali.InlineSmaliCompilerKt.toInstructions(InlineSmaliCompiler.kt:72)
        at app.revanced.patcher.util.smali.InlineSmaliCompilerKt.toInstruction(InlineSmaliCompiler.kt:84)
        at app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction(InstructionExtensions.kt:273)
        at app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch.close(LithoFilterPatch.kt:144)
        at app.revanced.patcher.Patcher$executePatches$1.invokeSuspend(Patcher.kt:379)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlin.sequences.SequenceBuilderIterator.hasNext(SequenceBuilder.kt:129)
        at app.revanced.utils.patcher.PatcherKt.applyPatchesVerbose(Patcher.kt:84)
        at app.revanced.cli.patcher.Patcher.start$revanced_cli(Patcher.kt:19)
        at app.revanced.cli.command.MainCommand.run(MainCommand.kt:166)
        at picocli.CommandLine.executeUserObject(CommandLine.java:2026)
        at picocli.CommandLine.access$1500(CommandLine.java:148)
        at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2461)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2453)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2415)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273)
        at picocli.CommandLine$RunLast.execute(CommandLine.java:2417)
        at picocli.CommandLine.execute(CommandLine.java:2170)
        at app.revanced.cli.main.MainKt.main(Main.kt:7)

SEVERE: LithoFilterPatch failed:
java.util.NoSuchElementException: Collection is empty.
        at kotlin.collections.CollectionsKt___CollectionsKt.first(_Collections.kt:201)
        at app.revanced.patcher.util.smali.InlineSmaliCompiler$Companion.compile(InlineSmaliCompiler.kt:58)
        at app.revanced.patcher.util.smali.InlineSmaliCompilerKt.toInstructions(InlineSmaliCompiler.kt:72)
        at app.revanced.patcher.util.smali.InlineSmaliCompilerKt.toInstruction(InlineSmaliCompiler.kt:84)
        at app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction(InstructionExtensions.kt:273)
        at app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch.close(LithoFilterPatch.kt:144)
        at app.revanced.patcher.Patcher$executePatches$1.invokeSuspend(Patcher.kt:379)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlin.sequences.SequenceBuilderIterator.hasNext(SequenceBuilder.kt:129)
        at app.revanced.utils.patcher.PatcherKt.applyPatchesVerbose(Patcher.kt:84)
        at app.revanced.cli.patcher.Patcher.start$revanced_cli(Patcher.kt:19)
        at app.revanced.cli.command.MainCommand.run(MainCommand.kt:166)
        at picocli.CommandLine.executeUserObject(CommandLine.java:2026)
        at picocli.CommandLine.access$1500(CommandLine.java:148)
        at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2461)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2453)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2415)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273)
        at picocli.CommandLine$RunLast.execute(CommandLine.java:2417)
        at picocli.CommandLine.execute(CommandLine.java:2170)
        at app.revanced.cli.main.MainKt.main(Main.kt:7)

Currently there are only 5 component filters, so this is not a problem right now

I just left a comment because I thought I should inform you, the repo's maintainer


companion object {
private val MethodFingerprint.patternScanResult
get() = result!!.scanResult.patternScanResult!!

val MethodFingerprint.patternScanEndIndex
private val MethodFingerprint.patternScanEndIndex
get() = patternScanResult.endIndex

val MethodFingerprint.patternScanStartIndex
private val MethodFingerprint.patternScanStartIndex
get() = patternScanResult.startIndex

val Instruction.descriptor
private val Instruction.descriptor
get() = (this as ReferenceInstruction).reference.toString()

const val FILTER_METHOD_DESCRIPTOR =
private const val FILTER_METHOD_DESCRIPTOR =
"Lapp/revanced/integrations/patches/components/LithoFilterPatch;" +
"->filter(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/nio/ByteBuffer;)Z"

internal lateinit var addFilter: (String) -> Unit
private set

private var filterCount = 0
}
}

2 comments on commit fc69491

@johnconner122
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments filter is missing!

@oSumAtrIX
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnconner122 Thanks, please open an issue or PR

Please sign in to comment.