Compare commits

..

4 Commits

Author SHA1 Message Date
semantic-release-bot
1a8146dbc8 chore: Release v5.33.0-dev.10 [skip ci]
# [5.33.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.9...v5.33.0-dev.10) (2025-08-04)

### Bug Fixes

* **YouTube - Video quality:** Fix wrong qualities sometimes shown in player button dialog ([178eed7](178eed7fcd))
2025-08-04 17:23:50 +00:00
LisoUseInAIKyrios
178eed7fcd fix(YouTube - Video quality): Fix wrong qualities sometimes shown in player button dialog 2025-08-04 13:21:02 -04:00
semantic-release-bot
621292644c chore: Release v5.33.0-dev.9 [skip ci]
# [5.33.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.8...v5.33.0-dev.9) (2025-08-04)

### Bug Fixes

* **YouTube - Force original audio:** Disable a/b feature flag that forces localized audio ([#5582](https://github.com/ReVanced/revanced-patches/issues/5582)) ([1dd01cf](1dd01cf54a))
2025-08-04 01:27:12 +00:00
LisoUseInAIKyrios
1dd01cf54a fix(YouTube - Force original audio): Disable a/b feature flag that forces localized audio (#5582) 2025-08-03 21:23:27 -04:00
8 changed files with 167 additions and 68 deletions

View File

@@ -1,3 +1,17 @@
# [5.33.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.9...v5.33.0-dev.10) (2025-08-04)
### Bug Fixes
* **YouTube - Video quality:** Fix wrong qualities sometimes shown in player button dialog ([7378ae3](https://github.com/ReVanced/revanced-patches/commit/7378ae3c5fc88f91bf5cd6db47c6cd170a8c5a4f))
# [5.33.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.8...v5.33.0-dev.9) (2025-08-04)
### Bug Fixes
* **YouTube - Force original audio:** Disable a/b feature flag that forces localized audio ([#5582](https://github.com/ReVanced/revanced-patches/issues/5582)) ([9fe13ee](https://github.com/ReVanced/revanced-patches/commit/9fe13ee1af104c009efd19b826adef375e48e191))
# [5.33.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.7...v5.33.0-dev.8) (2025-08-03) # [5.33.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.7...v5.33.0-dev.8) (2025-08-03)

View File

@@ -24,6 +24,16 @@ public class ForceOriginalAudioPatch {
} }
} }
/**
* Injection point.
*/
public static boolean ignoreDefaultAudioStream(boolean original) {
if (Settings.FORCE_ORIGINAL_AUDIO.get()) {
return false;
}
return original;
}
/** /**
* Injection point. * Injection point.
*/ */
@@ -50,7 +60,6 @@ public class ForceOriginalAudioPatch {
return isOriginal; return isOriginal;
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "isDefaultAudioStream failure", ex); Logger.printException(() -> "isDefaultAudioStream failure", ex);
return isDefault; return isDefault;
} }
} }

View File

@@ -8,7 +8,6 @@ import androidx.annotation.Nullable;
import com.google.android.libraries.youtube.innertube.model.media.VideoQuality; import com.google.android.libraries.youtube.innertube.model.media.VideoQuality;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
@@ -45,13 +44,11 @@ public class RememberVideoQualityPatch {
private static final IntegerSetting shortsQualityWifi = Settings.SHORTS_QUALITY_DEFAULT_WIFI; private static final IntegerSetting shortsQualityWifi = Settings.SHORTS_QUALITY_DEFAULT_WIFI;
private static final IntegerSetting shortsQualityMobile = Settings.SHORTS_QUALITY_DEFAULT_MOBILE; private static final IntegerSetting shortsQualityMobile = Settings.SHORTS_QUALITY_DEFAULT_MOBILE;
private static boolean qualityNeedsUpdating;
/** /**
* The available qualities of the current video. * The available qualities of the current video.
*/ */
@Nullable @Nullable
private static List<VideoQuality> currentQualities; private static VideoQuality[] currentQualities;
/** /**
* The current quality of the video playing. * The current quality of the video playing.
@@ -67,7 +64,7 @@ public class RememberVideoQualityPatch {
private static VideoQualityMenuInterface currentMenuInterface; private static VideoQualityMenuInterface currentMenuInterface;
@Nullable @Nullable
public static List<VideoQuality> getCurrentQualities() { public static VideoQuality[] getCurrentQualities() {
return currentQualities; return currentQualities;
} }
@@ -138,11 +135,11 @@ public class RememberVideoQualityPatch {
Utils.verifyOnMainThread(); Utils.verifyOnMainThread();
currentMenuInterface = menu; currentMenuInterface = menu;
final boolean availableQualitiesChanged = currentQualities == null final boolean availableQualitiesChanged = (currentQualities == null)
|| currentQualities.size() != qualities.length; || !Arrays.equals(currentQualities, qualities);
if (availableQualitiesChanged) { if (availableQualitiesChanged) {
currentQualities = Arrays.asList(qualities); currentQualities = qualities;
Logger.printDebug(() -> "VideoQualities: " + currentQualities); Logger.printDebug(() -> "VideoQualities: " + Arrays.toString(currentQualities));
} }
VideoQuality updatedCurrentQuality = qualities[originalQualityIndex]; VideoQuality updatedCurrentQuality = qualities[originalQualityIndex];
@@ -155,17 +152,12 @@ public class RememberVideoQualityPatch {
VideoQualityDialogButton.updateButtonIcon(updatedCurrentQuality); VideoQualityDialogButton.updateButtonIcon(updatedCurrentQuality);
} }
final int preferredQuality = getDefaultQualityResolution();
if (preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) {
return originalQualityIndex; // Nothing to do.
}
// After changing videos the qualities can initially be for the prior video. // After changing videos the qualities can initially be for the prior video.
// If the qualities have changed and the default is not auto then an update is needed. // If the qualities have changed and the default is not auto then an update is needed.
if (!qualityNeedsUpdating && !availableQualitiesChanged) { final int preferredQuality = getDefaultQualityResolution();
return originalQualityIndex; if (preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE || !availableQualitiesChanged) {
return originalQualityIndex; // Nothing to do.
} }
qualityNeedsUpdating = false;
// Find the highest quality that is equal to or less than the preferred. // Find the highest quality that is equal to or less than the preferred.
int i = 0; int i = 0;
@@ -213,7 +205,7 @@ public class RememberVideoQualityPatch {
Logger.printDebug(() -> "Cannot save default quality, qualities is null"); Logger.printDebug(() -> "Cannot save default quality, qualities is null");
return; return;
} }
VideoQuality quality = currentQualities.get(userSelectedQualityIndex); VideoQuality quality = currentQualities[userSelectedQualityIndex];
saveDefaultQuality(quality.patch_getResolution()); saveDefaultQuality(quality.patch_getResolution());
} }
} catch (Exception ex) { } catch (Exception ex) {
@@ -243,7 +235,6 @@ public class RememberVideoQualityPatch {
currentQualities = null; currentQualities = null;
currentQuality = null; currentQuality = null;
currentMenuInterface = null; currentMenuInterface = null;
qualityNeedsUpdating = true;
// Hide the quality button until playback starts and the qualities are available. // Hide the quality button until playback starts and the qualities are available.
VideoQualityDialogButton.updateButtonIcon(null); VideoQualityDialogButton.updateButtonIcon(null);

View File

@@ -127,7 +127,7 @@ public class VideoQualityDialogButton {
}, },
view -> { view -> {
try { try {
List<VideoQuality> qualities = RememberVideoQualityPatch.getCurrentQualities(); VideoQuality[] qualities = RememberVideoQualityPatch.getCurrentQualities();
VideoQualityMenuInterface menu = RememberVideoQualityPatch.getCurrentMenuInterface(); VideoQualityMenuInterface menu = RememberVideoQualityPatch.getCurrentMenuInterface();
if (qualities == null || menu == null) { if (qualities == null || menu == null) {
Logger.printDebug(() -> "Cannot reset quality, videoQualities is null"); Logger.printDebug(() -> "Cannot reset quality, videoQualities is null");
@@ -186,13 +186,13 @@ public class VideoQualityDialogButton {
*/ */
private static void showVideoQualityDialog(Context context) { private static void showVideoQualityDialog(Context context) {
try { try {
List<VideoQuality> currentQualities = RememberVideoQualityPatch.getCurrentQualities(); VideoQuality[] currentQualities = RememberVideoQualityPatch.getCurrentQualities();
VideoQuality currentQuality = RememberVideoQualityPatch.getCurrentQuality(); VideoQuality currentQuality = RememberVideoQualityPatch.getCurrentQuality();
if (currentQualities == null || currentQuality == null) { if (currentQualities == null || currentQuality == null) {
Logger.printDebug(() -> "Cannot show qualities dialog, videoQualities is null"); Logger.printDebug(() -> "Cannot show qualities dialog, videoQualities is null");
return; return;
} }
if (currentQualities.size() < 2) { if (currentQualities.length < 2) {
// Should never happen. // Should never happen.
Logger.printException(() -> "Cannot show qualities dialog, no qualities available"); Logger.printException(() -> "Cannot show qualities dialog, no qualities available");
return; return;
@@ -205,9 +205,17 @@ public class VideoQualityDialogButton {
} }
// -1 adjustment for automatic quality at first index. // -1 adjustment for automatic quality at first index.
final int listViewSelectedIndex = currentQualities.indexOf(currentQuality) - 1; int listViewSelectedIndex = 0;
for (VideoQuality quality : currentQualities) {
if (quality == currentQuality) {
// -1 adjustment for the missing automatic quality in the dialog list.
listViewSelectedIndex--;
break;
}
listViewSelectedIndex++;
}
List<String> qualityLabels = new ArrayList<>(currentQualities.size() - 1); List<String> qualityLabels = new ArrayList<>(currentQualities.length - 1);
for (VideoQuality availableQuality : currentQualities) { for (VideoQuality availableQuality : currentQualities) {
if (availableQuality.patch_getResolution() != AUTOMATIC_VIDEO_QUALITY_VALUE) { if (availableQuality.patch_getResolution() != AUTOMATIC_VIDEO_QUALITY_VALUE) {
qualityLabels.add(availableQuality.patch_getQualityName()); qualityLabels.add(availableQuality.patch_getQualityName());
@@ -310,7 +318,7 @@ public class VideoQualityDialogButton {
listView.setOnItemClickListener((parent, view, which, id) -> { listView.setOnItemClickListener((parent, view, which, id) -> {
try { try {
final int originalIndex = which + 1; // Adjust for automatic. final int originalIndex = which + 1; // Adjust for automatic.
VideoQuality selectedQuality = currentQualities.get(originalIndex); VideoQuality selectedQuality = currentQualities[originalIndex];
Logger.printDebug(() -> "User clicked on quality: " + selectedQuality); Logger.printDebug(() -> "User clicked on quality: " + selectedQuality);
if (RememberVideoQualityPatch.shouldRememberVideoQuality()) { if (RememberVideoQualityPatch.shouldRememberVideoQuality()) {

View File

@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true org.gradle.parallel = true
android.useAndroidX = true android.useAndroidX = true
kotlin.code.style = official kotlin.code.style = official
version = 5.33.0-dev.8 version = 5.33.0-dev.10

View File

@@ -1,23 +1,27 @@
package app.revanced.patches.youtube.video.audio package app.revanced.patches.youtube.video.audio
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
internal val streamingModelBuilderFingerprint = fingerprint { internal val formatStreamModelToStringFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L") returns("Ljava/lang/String;")
strings("vprng") custom { method, classDef ->
method.name == "toString" && classDef.type ==
"Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;"
}
} }
internal val menuItemAudioTrackFingerprint = fingerprint { internal const val AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG = 45666189L
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("L") internal val selectAudioStreamFingerprint = fingerprint {
returns("V") accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
strings("menu_item_audio_track") returns("L")
custom { method, _ ->
method.parameters.size > 2 // Method has a large number of parameters and may change.
&& method.parameters[1].type == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;"
&& method.containsLiteralInstruction(AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG)
}
} }
internal val audioStreamingTypeSelector = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returns("L")
strings("raw") // String is not unique
}

View File

@@ -5,22 +5,22 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference import app.revanced.util.findMethodFromToString
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertLiteralOverride
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableField import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
@@ -37,6 +37,7 @@ val forceOriginalAudioPatch = bytecodePatch(
sharedExtensionPatch, sharedExtensionPatch,
settingsPatch, settingsPatch,
addResourcesPatch, addResourcesPatch,
versionCheckPatch
) )
compatibleWith( compatibleWith(
@@ -60,29 +61,25 @@ val forceOriginalAudioPatch = bytecodePatch(
) )
) )
fun Method.firstFormatStreamingModelCall( // Disable feature flag that ignores the default track flag
returnType: String = "Ljava/lang/String;" // and instead overrides to the user region language.
): MutableMethod { if (is_20_07_or_greater) {
val audioTrackIdIndex = indexOfFirstInstructionOrThrow { selectAudioStreamFingerprint.method.insertLiteralOverride(
val reference = getReference<MethodReference>() AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG,
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;" "$EXTENSION_CLASS_DESCRIPTOR->ignoreDefaultAudioStream(Z)Z"
&& reference.returnType == returnType )
}
return navigate(this).to(audioTrackIdIndex).stop()
} }
// Accessor methods of FormatStreamModel have no string constants and val isDefaultAudioTrackMethod = formatStreamModelToStringFingerprint.originalMethod
// opcodes are identical to other methods in the same class, .findMethodFromToString("isDefaultAudioTrack=")
// so must walk from another class that use the methods. val audioTrackDisplayNameMethod = formatStreamModelToStringFingerprint.originalMethod
val isDefaultMethod = streamingModelBuilderFingerprint.originalMethod.firstFormatStreamingModelCall("Z") .findMethodFromToString("audioTrackDisplayName=")
val audioTrackIdMethod = menuItemAudioTrackFingerprint.originalMethod.firstFormatStreamingModelCall() val audioTrackIdMethod = formatStreamModelToStringFingerprint.originalMethod
val audioTrackDisplayNameMethod = audioStreamingTypeSelector.originalMethod.firstFormatStreamingModelCall() .findMethodFromToString("audioTrackId=")
val formatStreamModelClass = proxy(classes.first {
it.type == audioTrackIdMethod.definingClass
}).mutableClass
formatStreamModelClass.apply { proxy(classes.first {
it.type == audioTrackIdMethod.definingClass
}).mutableClass.apply {
// Add a new field to store the override. // Add a new field to store the override.
val helperFieldName = "isDefaultAudioTrackOverride" val helperFieldName = "isDefaultAudioTrackOverride"
fields.add( fields.add(
@@ -103,7 +100,7 @@ val forceOriginalAudioPatch = bytecodePatch(
// Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed. // Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed.
val helperMethodClass = type val helperMethodClass = type
val helperMethodName = "extension_isDefaultAudioTrack" val helperMethodName = "patch_isDefaultAudioTrack"
val helperMethod = ImmutableMethod( val helperMethod = ImmutableMethod(
helperMethodClass, helperMethodClass,
helperMethodName, helperMethodName,
@@ -143,7 +140,7 @@ val forceOriginalAudioPatch = bytecodePatch(
methods.add(helperMethod) methods.add(helperMethod)
// Modify isDefaultAudioTrack() to call extension helper method. // Modify isDefaultAudioTrack() to call extension helper method.
isDefaultMethod.apply { isDefaultAudioTrackMethod.apply {
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN) val index = indexOfFirstInstructionOrThrow(Opcode.RETURN)
val register = getInstruction<OneRegisterInstruction>(index).registerA val register = getInstruction<OneRegisterInstruction>(index).registerA

View File

@@ -32,7 +32,10 @@ import com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstructio
import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.Reference import com.android.tools.smali.dexlib2.iface.reference.Reference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
import com.android.tools.smali.dexlib2.immutable.ImmutableField import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.util.MethodUtil import com.android.tools.smali.dexlib2.util.MethodUtil
import java.util.EnumSet import java.util.EnumSet
@@ -171,6 +174,79 @@ internal val Instruction.isBranchInstruction: Boolean
internal val Instruction.isReturnInstruction: Boolean internal val Instruction.isReturnInstruction: Boolean
get() = this.opcode in returnOpcodes get() = this.opcode in returnOpcodes
/**
* Find the instruction index used for a toString() StringBuilder write of a given String name.
*
* @param fieldName The name of the field to find. Partial matches are allowed.
*/
private fun Method.findInstructionIndexFromToString(fieldName: String) : Int {
val stringIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
reference?.string?.contains(fieldName) == true
}
if (stringIndex < 0) {
throw IllegalArgumentException("Could not find usage of string: '$fieldName'")
}
val stringRegister = getInstruction<OneRegisterInstruction>(stringIndex).registerA
// Find use of the string with a StringBuilder.
val stringUsageIndex = indexOfFirstInstruction(stringIndex) {
val reference = getReference<MethodReference>()
reference?.definingClass == "Ljava/lang/StringBuilder;" &&
(this as? FiveRegisterInstruction)?.registerD == stringRegister
}
if (stringUsageIndex < 0) {
throw IllegalArgumentException("Could not find StringBuilder usage in: $this")
}
// Find the next usage of StringBuilder, which should be the desired field.
val fieldUsageIndex = indexOfFirstInstruction(stringUsageIndex + 1) {
val reference = getReference<MethodReference>()
reference?.definingClass == "Ljava/lang/StringBuilder;" && reference.name == "append"
}
if (fieldUsageIndex < 0) {
// Should never happen.
throw IllegalArgumentException("Could not find StringBuilder append usage in: $this")
}
val fieldUsageRegister = getInstruction<FiveRegisterInstruction>(fieldUsageIndex).registerD
// Look backwards up the method to find the instruction that sets the register.
var fieldSetIndex = indexOfFirstInstructionReversedOrThrow(fieldUsageIndex - 1) {
fieldUsageRegister == writeRegister
}
// If the field is a method call, then adjust from MOVE_RESULT to the method call.
val fieldSetOpcode = getInstruction(fieldSetIndex).opcode
if (fieldSetOpcode == MOVE_RESULT ||
fieldSetOpcode == MOVE_RESULT_WIDE ||
fieldSetOpcode == MOVE_RESULT_OBJECT) {
fieldSetIndex--
}
return fieldSetIndex
}
/**
* Find the method used for a toString() StringBuilder write of a given String name.
*
* @param fieldName The name of the field to find. Partial matches are allowed.
*/
context(BytecodePatchContext)
internal fun Method.findMethodFromToString(fieldName: String) : MutableMethod {
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
return navigate(this).to(methodUsageIndex).stop()
}
/**
* Find the field used for a toString() StringBuilder write of a given String name.
*
* @param fieldName The name of the field to find. Partial matches are allowed.
*/
internal fun Method.findFieldFromToString(fieldName: String) : FieldReference {
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
return getInstruction<ReferenceInstruction>(methodUsageIndex).getReference<FieldReference>()!!
}
/** /**
* Adds public [AccessFlags] and removes private and protected flags (if present). * Adds public [AccessFlags] and removes private and protected flags (if present).
*/ */
@@ -594,7 +670,7 @@ fun Method.indexOfFirstInstructionReversed(targetOpcode: Opcode): Int = indexOfF
/** /**
* Get the index of matching instruction, * Get the index of matching instruction,
* starting from and [startIndex] and searching down. * starting from [startIndex] and searching down.
* *
* @param startIndex Optional starting index to search down from. Searching includes the start index. * @param startIndex Optional starting index to search down from. Searching includes the start index.
* @return The index of the instruction. * @return The index of the instruction.
@@ -617,7 +693,7 @@ fun Method.indexOfFirstInstructionReversedOrThrow(targetOpcode: Opcode): Int = i
/** /**
* Get the index of matching instruction, * Get the index of matching instruction,
* starting from and [startIndex] and searching down. * starting from [startIndex] and searching down.
* *
* @param startIndex Optional starting index to search down from. Searching includes the start index. * @param startIndex Optional starting index to search down from. Searching includes the start index.
* @return The index of the instruction. * @return The index of the instruction.