@@ -9,13 +9,13 @@ import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
99import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
1010import app.revanced.patcher.patch.bytecodePatch
1111import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
12+ import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater
1213import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
1314import app.revanced.patches.youtube.misc.playservice.is_20_05_or_greater
1415import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
1516import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
1617import app.revanced.util.addInstructionsAtControlFlowLabel
1718import app.revanced.util.findFreeRegister
18- import app.revanced.util.findInstructionIndicesReversedOrThrow
1919import app.revanced.util.getReference
2020import app.revanced.util.indexOfFirstInstructionOrThrow
2121import app.revanced.util.indexOfFirstInstructionReversedOrThrow
@@ -24,7 +24,6 @@ import com.android.tools.smali.dexlib2.Opcode
2424import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
2525import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
2626import com.android.tools.smali.dexlib2.iface.reference.FieldReference
27- import com.android.tools.smali.dexlib2.iface.reference.MethodReference
2827
2928lateinit var addLithoFilter: (String ) -> Unit
3029 private set
@@ -66,17 +65,11 @@ val lithoFilterPatch = bytecodePatch(
6665 * }
6766 * }
6867 *
69- * class ComponentContextParser {
70- * public Component parseComponent () {
68+ * class CreateComponentClass {
69+ * public Component createComponent () {
7170 * ...
7271 *
73- * // Checks if the component should be filtered.
74- * // Sets a thread local with the filtering result.
75- * extensionClass.filter(identifier, pathBuilder); // Inserted by this patch.
76- *
77- * ...
78- *
79- * if (extensionClass.shouldFilter()) { // Inserted by this patch.
72+ * if (extensionClass.shouldFilter(identifier, path)) { // Inserted by this patch.
8073 * return emptyComponent;
8174 * }
8275 * return originalUnpatchedComponent; // Original code.
@@ -116,95 +109,68 @@ val lithoFilterPatch = bytecodePatch(
116109 // Allow the method to run to completion, and override the
117110 // return value with an empty component if it should be filtered.
118111 // It is important to allow the original code to always run to completion,
119- // otherwise memory leaks and poor app performance can occur.
120- //
121- // The extension filtering result needs to be saved off somewhere, but cannot
122- // save to a class field since the target class is called by multiple threads.
123- // It would be great if there was a way to change the register count of the
124- // method implementation and save the result to a high register to later use
125- // in the method, but there is no simple way to do that.
126- // Instead save the extension filter result to a thread local and check the
127- // filtering result at each method return index.
128- // String field for the litho identifier.
129- componentContextParserFingerprint.method.apply {
130- val conversionContextClass = conversionContextFingerprintToString.originalClassDef
131-
132- val conversionContextIdentifierField = componentContextSubParserFingerprint.match(
133- componentContextParserFingerprint.originalClassDef
134- ).let {
135- // Identifier field is loaded just before the string declaration.
136- val index = it.method.indexOfFirstInstructionReversedOrThrow(
137- it.stringMatches!! .first().index
138- ) {
139- val reference = getReference<FieldReference >()
140- reference?.definingClass == conversionContextClass.type
141- && reference.type == " Ljava/lang/String;"
142- }
143- it.method.getInstruction<ReferenceInstruction >(index).getReference<FieldReference >()
112+ // otherwise high memory usage and poor app performance can occur.
113+
114+ // Find the identifier/path fields of the conversion context.
115+ val conversionContextIdentifierField = componentContextParserFingerprint.let {
116+ // Identifier field is loaded just before the string declaration.
117+ val index = it.method.indexOfFirstInstructionReversedOrThrow(
118+ it.stringMatches!! .first().index
119+ ) {
120+ val reference = getReference<FieldReference >()
121+ reference?.definingClass == conversionContextFingerprintToString.originalClassDef.type
122+ && reference.type == " Ljava/lang/String;"
144123 }
145124
146- // StringBuilder field for the litho path.
147- val conversionContextPathBuilderField = conversionContextClass.fields
148- .single { field -> field.type == " Ljava/lang/StringBuilder;" }
125+ it.method.getInstruction<ReferenceInstruction >(index).getReference<FieldReference >()!!
126+ }
149127
150- val conversionContextResultIndex = indexOfFirstInstructionOrThrow {
151- val reference = getReference<MethodReference >()
152- reference?.returnType == conversionContextClass.type
153- } + 1
128+ val conversionContextPathBuilderField = conversionContextFingerprintToString.originalClassDef
129+ .fields.single { field -> field.type == " Ljava/lang/StringBuilder;" }
154130
155- val conversionContextResultRegister = getInstruction<OneRegisterInstruction >(
156- conversionContextResultIndex
157- ).registerA
131+ // Find class and methods to create an empty component.
132+ val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.single {
133+ // The only static method in the class.
134+ method -> AccessFlags .STATIC .isSet(method.accessFlags)
135+ }
136+ val emptyComponentField = classBy {
137+ // Only one field that matches.
138+ it.type == builderMethodDescriptor.returnType
139+ }!! .immutableClass.fields.single()
140+
141+ componentCreateFingerprint.method.apply {
142+ val insertIndex = if (is_19_17_or_greater) {
143+ indexOfFirstInstructionOrThrow(Opcode .RETURN_OBJECT )
144+ } else {
145+ // 19.16 clobbers p2 so must check at start of the method and not at the return index.
146+ 0
147+ }
158148
159- val identifierRegister = findFreeRegister(
160- conversionContextResultIndex, conversionContextResultRegister
161- )
162- val stringBuilderRegister = findFreeRegister(
163- conversionContextResultIndex, conversionContextResultRegister, identifierRegister
164- )
149+ val freeRegister = findFreeRegister(insertIndex)
150+ val identifierRegister = findFreeRegister(insertIndex, freeRegister)
151+ val pathRegister = findFreeRegister(insertIndex, freeRegister, identifierRegister)
165152
166- // Check if the component should be filtered, and save the result to a thread local.
167153 addInstructionsAtControlFlowLabel(
168- conversionContextResultIndex + 1 ,
154+ insertIndex ,
169155 """
170- iget-object v$identifierRegister , v$conversionContextResultRegister , $conversionContextIdentifierField
171- iget-object v$stringBuilderRegister , v$conversionContextResultRegister , $conversionContextPathBuilderField
172- invoke-static { v$identifierRegister , v$stringBuilderRegister }, $EXTENSION_CLASS_DESCRIPTOR ->filter(Ljava/lang/String;Ljava/lang/StringBuilder;)V
156+ move-object/from16 v$freeRegister , p2
157+ iget-object v$identifierRegister , v$freeRegister , $conversionContextIdentifierField
158+ iget-object v$pathRegister , v$freeRegister , $conversionContextPathBuilderField
159+ invoke-static { v$identifierRegister , v$pathRegister }, $EXTENSION_CLASS_DESCRIPTOR ->shouldFilter(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
160+ move-result v$freeRegister
161+ if-eqz v$freeRegister , :unfiltered
162+
163+ # Return an empty component
164+ move-object/from16 v$freeRegister , p1
165+ invoke-static { v$freeRegister }, $builderMethodDescriptor
166+ move-result-object v$freeRegister
167+ iget-object v$freeRegister , v$freeRegister , $emptyComponentField
168+ return-object v$freeRegister
169+
170+ :unfiltered
171+ nop
173172 """
174173 )
175-
176- // Get the only static method in the class.
177- val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.single {
178- method -> AccessFlags .STATIC .isSet(method.accessFlags)
179- }
180- // Only one field.
181- val emptyComponentField = classBy { classDef ->
182- classDef.type == builderMethodDescriptor.returnType
183- }!! .immutableClass.fields.single()
184-
185- // Check at each return value if the component is filtered,
186- // and return an empty component if filtering is needed.
187- findInstructionIndicesReversedOrThrow(Opcode .RETURN_OBJECT ).forEach { returnIndex ->
188- val freeRegister = findFreeRegister(returnIndex)
189-
190- addInstructionsAtControlFlowLabel(
191- returnIndex,
192- """
193- invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR ->shouldFilter()Z
194- move-result v$freeRegister
195- if-eqz v$freeRegister , :unfiltered
196-
197- move-object/from16 v$freeRegister , p1
198- invoke-static { v$freeRegister }, $builderMethodDescriptor
199- move-result-object v$freeRegister
200- iget-object v$freeRegister , v$freeRegister , $emptyComponentField
201- return-object v$freeRegister
202-
203- :unfiltered
204- nop
205- """
206- )
207- }
208174 }
209175
210176 // endregion
0 commit comments