diff --git a/api/revanced-patcher.api b/api/revanced-patcher.api index 5e68eca..689e331 100644 --- a/api/revanced-patcher.api +++ b/api/revanced-patcher.api @@ -4,15 +4,15 @@ public final class app/revanced/patcher/AnyInstruction : app/revanced/patcher/In } public final class app/revanced/patcher/CheckCastFilter : app/revanced/patcher/OpcodeFilter { + public final fun getComparison ()Lapp/revanced/patcher/StringComparisonType; public final fun getType ()Lkotlin/jvm/functions/Function0; public fun matches (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Z - public final fun setType (Lkotlin/jvm/functions/Function0;)V } public final class app/revanced/patcher/FieldAccessFilter : app/revanced/patcher/OpcodesFilter { - public final fun getDefiningClass ()Lkotlin/jvm/functions/Function0; - public final fun getName ()Lkotlin/jvm/functions/Function0; - public final fun getType ()Lkotlin/jvm/functions/Function0; + public final fun getDefiningClass ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getType ()Ljava/lang/String; public fun matches (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Z } @@ -93,19 +93,21 @@ public final class app/revanced/patcher/InstructionFilterKt { public static final fun anyInstruction ([Lapp/revanced/patcher/InstructionFilter;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/AnyInstruction; public static synthetic fun anyInstruction$default ([Lapp/revanced/patcher/InstructionFilter;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/AnyInstruction; public static final fun checkCast (Ljava/lang/String;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/CheckCastFilter; + public static final fun checkCast (Ljava/lang/String;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/CheckCastFilter; public static final fun checkCast (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/CheckCastFilter; + public static final fun checkCast (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/CheckCastFilter; public static synthetic fun checkCast$default (Ljava/lang/String;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/CheckCastFilter; + public static synthetic fun checkCast$default (Ljava/lang/String;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/CheckCastFilter; public static synthetic fun checkCast$default (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/CheckCastFilter; + public static synthetic fun checkCast$default (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/CheckCastFilter; public static final fun fieldAccess (Ljava/lang/String;Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/FieldAccessFilter; public static final fun fieldAccess (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/FieldAccessFilter; public static final fun fieldAccess (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/FieldAccessFilter; public static final fun fieldAccess (Ljava/lang/String;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/FieldAccessFilter; - public static final fun fieldAccess (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/FieldAccessFilter; public static synthetic fun fieldAccess$default (Ljava/lang/String;Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/FieldAccessFilter; public static synthetic fun fieldAccess$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/FieldAccessFilter; public static synthetic fun fieldAccess$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/FieldAccessFilter; public static synthetic fun fieldAccess$default (Ljava/lang/String;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/FieldAccessFilter; - public static synthetic fun fieldAccess$default (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/FieldAccessFilter; public static final fun literal (DLjava/util/List;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/LiteralFilter; public static final fun literal (FLjava/util/List;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/LiteralFilter; public static final fun literal (ILjava/util/List;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/LiteralFilter; @@ -120,22 +122,28 @@ public final class app/revanced/patcher/InstructionFilterKt { public static final fun methodCall (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/MethodCallFilter; public static final fun methodCall (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/MethodCallFilter; public static final fun methodCall (Ljava/lang/String;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/MethodCallFilter; - public static final fun methodCall (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/MethodCallFilter; public static synthetic fun methodCall$default (Ljava/lang/String;Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/MethodCallFilter; public static synthetic fun methodCall$default (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/MethodCallFilter; public static synthetic fun methodCall$default (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/MethodCallFilter; public static synthetic fun methodCall$default (Ljava/lang/String;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/MethodCallFilter; - public static synthetic fun methodCall$default (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/MethodCallFilter; public static final fun newInstance (Ljava/lang/String;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/NewInstanceFilter; + public static final fun newInstance (Ljava/lang/String;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/NewInstanceFilter; + public static final fun newInstance (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/NewInstanceFilter; + public static final fun newInstance (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/NewInstanceFilter; public static synthetic fun newInstance$default (Ljava/lang/String;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/NewInstanceFilter; - public static final fun newInstancetype (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/NewInstanceFilter; - public static synthetic fun newInstancetype$default (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/NewInstanceFilter; + public static synthetic fun newInstance$default (Ljava/lang/String;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/NewInstanceFilter; + public static synthetic fun newInstance$default (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/NewInstanceFilter; + public static synthetic fun newInstance$default (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/NewInstanceFilter; public static final fun opcode (Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/OpcodeFilter; public static synthetic fun opcode$default (Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/OpcodeFilter; - public static final fun string (Ljava/lang/String;Lapp/revanced/patcher/StringMatchType;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/StringFilter; - public static final fun string (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/StringMatchType;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/StringFilter; - public static synthetic fun string$default (Ljava/lang/String;Lapp/revanced/patcher/StringMatchType;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/StringFilter; - public static synthetic fun string$default (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/StringMatchType;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/StringFilter; + public static final fun string (Ljava/lang/String;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/StringFilter; + public static final fun string (Ljava/lang/String;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/StringFilter; + public static final fun string (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/StringFilter; + public static final fun string (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;)Lapp/revanced/patcher/StringFilter; + public static synthetic fun string$default (Ljava/lang/String;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/StringFilter; + public static synthetic fun string$default (Ljava/lang/String;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/StringFilter; + public static synthetic fun string$default (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/StringFilter; + public static synthetic fun string$default (Lkotlin/jvm/functions/Function0;Lapp/revanced/patcher/StringComparisonType;Lapp/revanced/patcher/InstructionLocation;ILjava/lang/Object;)Lapp/revanced/patcher/StringFilter; } public abstract interface class app/revanced/patcher/InstructionLocation { @@ -147,16 +155,29 @@ public final class app/revanced/patcher/InstructionLocation$MatchAfterAnywhere : public fun indexIsValidForMatching (II)Z } +public final class app/revanced/patcher/InstructionLocation$MatchAfterAtLeast : app/revanced/patcher/InstructionLocation { + public fun (I)V + public final fun getMinimumDistanceFromLastInstruction ()I + public fun indexIsValidForMatching (II)Z + public final fun setMinimumDistanceFromLastInstruction (I)V +} + public final class app/revanced/patcher/InstructionLocation$MatchAfterImmediately : app/revanced/patcher/InstructionLocation { public fun ()V public fun indexIsValidForMatching (II)Z } +public final class app/revanced/patcher/InstructionLocation$MatchAfterRange : app/revanced/patcher/InstructionLocation { + public fun (II)V + public final fun getMaximumDistanceFromLastInstruction ()I + public final fun getMinimumDistanceFromLastInstruction ()I + public fun indexIsValidForMatching (II)Z +} + public final class app/revanced/patcher/InstructionLocation$MatchAfterWithin : app/revanced/patcher/InstructionLocation { public fun (I)V public final fun getMatchDistance ()I public fun indexIsValidForMatching (II)Z - public final fun setMatchDistance (I)V } public final class app/revanced/patcher/InstructionLocation$MatchFirst : app/revanced/patcher/InstructionLocation { @@ -170,7 +191,6 @@ public abstract interface annotation class app/revanced/patcher/InternalApi : ja public final class app/revanced/patcher/LiteralFilter : app/revanced/patcher/OpcodesFilter { public final fun getLiteral ()Lkotlin/jvm/functions/Function0; public fun matches (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Z - public final fun setLiteral (Lkotlin/jvm/functions/Function0;)V } public final class app/revanced/patcher/Match { @@ -339,41 +359,36 @@ public final class app/revanced/patcher/MatchingKt { } public final class app/revanced/patcher/MethodCallFilter : app/revanced/patcher/OpcodesFilter { - public static final field Companion Lapp/revanced/patcher/MethodCallFilter$Companion; - public final fun getDefiningClass ()Lkotlin/jvm/functions/Function0; - public final fun getName ()Lkotlin/jvm/functions/Function0; - public final fun getParameters ()Lkotlin/jvm/functions/Function0; - public final fun getReturnType ()Lkotlin/jvm/functions/Function0; + public final fun getDefiningClass ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getParameters ()Ljava/util/List; + public final fun getReturnType ()Ljava/lang/String; public fun matches (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Z } -public final class app/revanced/patcher/MethodCallFilter$Companion { -} - public final class app/revanced/patcher/NewInstanceFilter : app/revanced/patcher/OpcodesFilter { + public final fun getComparison ()Lapp/revanced/patcher/StringComparisonType; public final fun getType ()Lkotlin/jvm/functions/Function0; public fun matches (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Z - public final fun setType (Lkotlin/jvm/functions/Function0;)V } public class app/revanced/patcher/OpcodeFilter : app/revanced/patcher/InstructionFilter { public fun (Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;)V + public synthetic fun (Lcom/android/tools/smali/dexlib2/Opcode;Lapp/revanced/patcher/InstructionLocation;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getLocation ()Lapp/revanced/patcher/InstructionLocation; public final fun getOpcode ()Lcom/android/tools/smali/dexlib2/Opcode; public fun matches (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Z } public class app/revanced/patcher/OpcodesFilter : app/revanced/patcher/InstructionFilter { - public static final field Companion Lapp/revanced/patcher/OpcodesFilter$Companion; + protected fun (Ljava/util/EnumSet;Lapp/revanced/patcher/InstructionLocation;)V + public synthetic fun (Ljava/util/EnumSet;Lapp/revanced/patcher/InstructionLocation;ILkotlin/jvm/internal/DefaultConstructorMarker;)V protected fun (Ljava/util/List;Lapp/revanced/patcher/InstructionLocation;)V public fun getLocation ()Lapp/revanced/patcher/InstructionLocation; public final fun getOpcodes ()Ljava/util/EnumSet; public fun matches (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Z } -public final class app/revanced/patcher/OpcodesFilter$Companion { -} - public final class app/revanced/patcher/PackageMetadata { public final fun getPackageName ()Ljava/lang/String; public final fun getPackageVersion ()Ljava/lang/String; @@ -417,22 +432,21 @@ public final class app/revanced/patcher/PatcherResult$PatchedResources { public final fun getResourcesApk ()Ljava/io/File; } -public final class app/revanced/patcher/StringFilter : app/revanced/patcher/OpcodesFilter { - public final fun getMatchType ()Lapp/revanced/patcher/StringMatchType; - public final fun getString ()Lkotlin/jvm/functions/Function0; - public fun matches (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Z - public final fun setMatchType (Lapp/revanced/patcher/StringMatchType;)V - public final fun setString (Lkotlin/jvm/functions/Function0;)V +public final class app/revanced/patcher/StringComparisonType : java/lang/Enum { + public static final field CONTAINS Lapp/revanced/patcher/StringComparisonType; + public static final field ENDS_WITH Lapp/revanced/patcher/StringComparisonType; + public static final field EQUALS Lapp/revanced/patcher/StringComparisonType; + public static final field STARTS_WITH Lapp/revanced/patcher/StringComparisonType; + public final fun compare (Ljava/lang/String;Ljava/lang/String;)Z + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patcher/StringComparisonType; + public static fun values ()[Lapp/revanced/patcher/StringComparisonType; } -public final class app/revanced/patcher/StringMatchType : java/lang/Enum { - public static final field CONTAINS Lapp/revanced/patcher/StringMatchType; - public static final field ENDS_WITH Lapp/revanced/patcher/StringMatchType; - public static final field EQUALS Lapp/revanced/patcher/StringMatchType; - public static final field STARTS_WITH Lapp/revanced/patcher/StringMatchType; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patcher/StringMatchType; - public static fun values ()[Lapp/revanced/patcher/StringMatchType; +public final class app/revanced/patcher/StringFilter : app/revanced/patcher/OpcodesFilter { + public final fun getComparison ()Lapp/revanced/patcher/StringComparisonType; + public final fun getString ()Lkotlin/jvm/functions/Function0; + public fun matches (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Z } public final class app/revanced/patcher/dex/mutable/MutableAnnotation : com/android/tools/smali/dexlib2/base/BaseAnnotation { @@ -794,6 +808,10 @@ public final class app/revanced/patcher/extensions/InstructionKt { public final class app/revanced/patcher/extensions/MethodKt { public static final fun accessFlags (Lcom/android/tools/smali/dexlib2/iface/Method;[Lcom/android/tools/smali/dexlib2/AccessFlags;)Z + public static final fun addInstruction (Lapp/revanced/patcher/dex/mutable/MutableMethod;ILcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V + public static final fun addInstruction (Lapp/revanced/patcher/dex/mutable/MutableMethod;ILjava/lang/String;)V + public static final fun addInstruction (Lapp/revanced/patcher/dex/mutable/MutableMethod;Lcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V + public static final fun addInstruction (Lapp/revanced/patcher/dex/mutable/MutableMethod;Ljava/lang/String;)V public static final fun addInstructions (Lapp/revanced/patcher/dex/mutable/MutableMethod;ILjava/lang/String;)V public static final fun addInstructions (Lapp/revanced/patcher/dex/mutable/MutableMethod;ILjava/util/List;)V public static final fun addInstructions (Lapp/revanced/patcher/dex/mutable/MutableMethod;Ljava/lang/String;)V @@ -818,10 +836,13 @@ public final class app/revanced/patcher/extensions/MethodKt { public static final fun getInstructionsOrNull (Lapp/revanced/patcher/dex/mutable/MutableMethod;)Ljava/util/List; public static final fun getInstructionsOrNull (Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Iterable; public static final fun newLabel (Lapp/revanced/patcher/dex/mutable/MutableMethod;I)Lcom/android/tools/smali/dexlib2/builder/Label; + public static final fun removeInstruction (Lapp/revanced/patcher/dex/mutable/MutableMethod;I)V public static final fun removeInstructions (Lapp/revanced/patcher/dex/mutable/MutableMethod;I)V public static final fun removeInstructions (Lapp/revanced/patcher/dex/mutable/MutableMethod;II)V public static final fun removeInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;I)V public static final fun removeInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;II)V + public static final fun replaceInstruction (Lapp/revanced/patcher/dex/mutable/MutableMethod;ILcom/android/tools/smali/dexlib2/builder/BuilderInstruction;)V + public static final fun replaceInstruction (Lapp/revanced/patcher/dex/mutable/MutableMethod;ILjava/lang/String;)V public static final fun replaceInstructions (Lapp/revanced/patcher/dex/mutable/MutableMethod;ILjava/lang/String;)V public static final fun replaceInstructions (Lapp/revanced/patcher/dex/mutable/MutableMethod;ILjava/util/List;)V public static final fun replaceInstructions (Lcom/android/tools/smali/dexlib2/builder/MutableMethodImplementation;ILjava/util/List;)V diff --git a/src/main/kotlin/app/revanced/patcher/Fingerprint.kt b/src/main/kotlin/app/revanced/patcher/Fingerprint.kt index a531161..35af339 100644 --- a/src/main/kotlin/app/revanced/patcher/Fingerprint.kt +++ b/src/main/kotlin/app/revanced/patcher/Fingerprint.kt @@ -108,13 +108,11 @@ class Fingerprint internal constructor( is FieldAccessFilter -> { val reference = instruction.fieldReference ?: return false - if (name != null && reference.name != name()) return false + if (name != null && reference.name != name) return false - if (type != null && !reference.type.startsWith(type())) return false + if (type != null && !reference.type.startsWith(type)) return false if (definingClass != null) { - val definingClass = definingClass() - if (!reference.definingClass.endsWith(definingClass)) // else, the method call is for 'this' class. if (!(definingClass == "this" && reference.definingClass == method.definingClass)) return false @@ -132,18 +130,17 @@ class Fingerprint internal constructor( is MethodCallFilter -> { val reference = instruction.methodReference ?: return false - if (name != null && reference.name != name()) return false + if (name != null && reference.name != name) return false - if (returnType != null && !reference.returnType.startsWith(returnType())) return false + if (returnType != null && !reference.returnType.startsWith(returnType)) return false if (parameters != null && !parametersStartsWith( - reference.parameterTypes, parameters() + reference.parameterTypes, + parameters ) ) return false if (definingClass != null) { - val definingClass = definingClass() - if (!reference.definingClass.endsWith(definingClass)) { // Check if 'this' defining class is used. // Would be nice if this also checked all super classes, @@ -168,11 +165,11 @@ class Fingerprint internal constructor( val string = instruction.stringReference?.string ?: return false val filterString = string() - when (matchType) { - StringMatchType.EQUALS -> string == filterString - StringMatchType.CONTAINS -> string.contains(filterString) - StringMatchType.STARTS_WITH -> string.startsWith(filterString) - StringMatchType.ENDS_WITH -> string.endsWith(filterString) + when (comparison) { + StringComparisonType.EQUALS -> string == filterString + StringComparisonType.CONTAINS -> string.contains(filterString) + StringComparisonType.STARTS_WITH -> string.startsWith(filterString) + StringComparisonType.ENDS_WITH -> string.endsWith(filterString) } } @@ -187,11 +184,19 @@ class Fingerprint internal constructor( is MatchAfterImmediately -> after { filter.evaluate(this) } is MatchAfterAnywhere -> add { filter.evaluate(this) } is MatchAfterWithin -> after(atLeast = 1, atMost = location.matchDistance) { - filter.evaluate( - this - ) + filter.evaluate(this) } + is MatchAfterAtLeast -> after( + atLeast = location.minimumDistanceFromLastInstruction, + atMost = Int.MAX_VALUE + ) { filter.evaluate(this) } + + is MatchAfterRange -> after( + atLeast = location.minimumDistanceFromLastInstruction, + atMost = location.maximumDistanceFromLastInstruction + ) { filter.evaluate(this) } + is MatchFirst -> first { filter.evaluate(this) } } } diff --git a/src/main/kotlin/app/revanced/patcher/InstructionFilter.kt b/src/main/kotlin/app/revanced/patcher/InstructionFilter.kt index e4cd79a..4545b95 100644 --- a/src/main/kotlin/app/revanced/patcher/InstructionFilter.kt +++ b/src/main/kotlin/app/revanced/patcher/InstructionFilter.kt @@ -1,5 +1,3 @@ -@file:Suppress("unused") - package app.revanced.patcher import app.revanced.patcher.FieldAccessFilter.Companion.parseJvmFieldAccess @@ -17,7 +15,7 @@ import java.util.* /** * Simple interface to control how much space is allowed between a previous - * [InstructionFilter match and the current [InstructionFilter]. + * [InstructionFilter] match and the current [InstructionFilter]. */ fun interface InstructionLocation { /** @@ -41,10 +39,8 @@ fun interface InstructionLocation { */ class MatchFirst() : InstructionLocation { override fun indexIsValidForMatching(previouslyMatchedIndex: Int, currentIndex: Int) : Boolean { - if (previouslyMatchedIndex >= 0) { - throw IllegalArgumentException( - "MatchFirst can only be used for the first instruction filter" - ) + require(previouslyMatchedIndex < 0) { + "MatchFirst can only be used for the first instruction filter" } return true } @@ -62,10 +58,8 @@ fun interface InstructionLocation { */ class MatchAfterImmediately() : InstructionLocation { override fun indexIsValidForMatching(previouslyMatchedIndex: Int, currentIndex: Int) : Boolean { - if (previouslyMatchedIndex < 0) { - throw IllegalArgumentException( - "MatchAfterImmediately cannot be used for the first instruction filter" - ) + require(previouslyMatchedIndex >= 0) { + "MatchAfterImmediately cannot be used for the first instruction filter" } return currentIndex - 1 == previouslyMatchedIndex } @@ -85,27 +79,127 @@ fun interface InstructionLocation { * instructions can exist between the previously matched instruction and * the current instruction filter. */ - class MatchAfterWithin(var matchDistance: Int) : InstructionLocation { + class MatchAfterWithin(val matchDistance: Int) : InstructionLocation { init { - if (matchDistance < 0) { - throw IllegalArgumentException("matchDistance must be non-negative") + require(matchDistance >= 0) { + "matchDistance must be non-negative" } } override fun indexIsValidForMatching(previouslyMatchedIndex: Int, currentIndex: Int) : Boolean { - if (previouslyMatchedIndex < 0) { - throw IllegalArgumentException( - "MatchAfterImmediately cannot be used for the first instruction filter" - ) + require(previouslyMatchedIndex >= 0) { + "MatchAfterImmediately cannot be used for the first instruction filter" } return currentIndex - previouslyMatchedIndex - 1 <= matchDistance } } + + /** + * Instruction index can occur only after a minimum number of unmatched instructions from the + * previous instruction match. Or if this is used with the first filter of a fingerprint then + * this can only match starting from a given instruction index. + * + * @param minimumDistanceFromLastInstruction The minimum number of unmatched instructions that + * must exist between this instruction and the last matched instruction. A value of 0 is + * functionally identical to [MatchAfterImmediately]. + */ + class MatchAfterAtLeast(var minimumDistanceFromLastInstruction: Int) : InstructionLocation { + init { + require(minimumDistanceFromLastInstruction >= 0) { + "minimumDistanceFromLastInstruction must >= 0" + } + } + + override fun indexIsValidForMatching(previouslyMatchedIndex: Int, currentIndex: Int) : Boolean { + return currentIndex - previouslyMatchedIndex - 1 >= minimumDistanceFromLastInstruction + } + } + + /** + * Functionally combines both [MatchAfterAtLeast] and [MatchAfterWithin] to give a bounded range + * where the next instruction must match relative to the previous matched instruction. + * + * Unlike [MatchAfterImmediately] or [MatchAfterWithin], this can be used for the first filter + * to constrain matching to a specific range starting from index 0. + * + * @param minimumDistanceFromLastInstruction The minimum number of unmatched instructions that + * must exist between this instruction and the last + * matched instruction. + * @param maximumDistanceFromLastInstruction The maximum number of unmatched instructions + * that can exist between this instruction and the + * last matched instruction. + */ + class MatchAfterRange( + val minimumDistanceFromLastInstruction: Int, + val maximumDistanceFromLastInstruction: Int + ) : InstructionLocation { + + private val minMatcher = MatchAfterAtLeast(minimumDistanceFromLastInstruction) + private val maxMatcher = MatchAfterWithin(maximumDistanceFromLastInstruction) + + init { + require(minimumDistanceFromLastInstruction <= maximumDistanceFromLastInstruction) { + "minimumDistanceFromLastInstruction must be <= maximumDistanceFromLastInstruction" + } + } + + override fun indexIsValidForMatching(previouslyMatchedIndex: Int, currentIndex: Int): Boolean { + // For the first filter, previouslyMatchedIndex will be -1, and both delegates + // will correctly enforce their own semantics starting from index 0. + return minMatcher.indexIsValidForMatching(previouslyMatchedIndex, currentIndex) && + maxMatcher.indexIsValidForMatching(previouslyMatchedIndex, currentIndex) + } + } } + /** - * Matches method [Instruction] objects, similar to how [Fingerprint] matches entire fingerprints. + * String comparison type. + */ +enum class StringComparisonType { + EQUALS, + CONTAINS, + STARTS_WITH, + ENDS_WITH; + + /** + * @param targetString The target string to search + * @param searchString To search for in the target string (or to compare entirely for equality). + */ + fun compare(targetString: String, searchString: String): Boolean { + return when (this) { + EQUALS -> targetString == searchString + CONTAINS -> targetString.contains(searchString) + STARTS_WITH -> targetString.startsWith(searchString) + ENDS_WITH -> targetString.endsWith(searchString) + } + } + + /** + * Throws [IllegalArgumentException] if the class type search string is invalid and can never match. + */ + internal fun validateSearchStringForClassType(classTypeSearchString: String) { + when (this) { + EQUALS -> { + STARTS_WITH.validateSearchStringForClassType(classTypeSearchString) + ENDS_WITH.validateSearchStringForClassType(classTypeSearchString) + } + CONTAINS -> Unit // Nothing to validate, anything goes. + STARTS_WITH -> require(classTypeSearchString.startsWith('L')) { + "Class type does not start with L: $classTypeSearchString" + } + ENDS_WITH -> require(classTypeSearchString.endsWith(';')) { + "Class type does not end with a semicolon: $classTypeSearchString" + } + } + } +} + + + +/** + * Matches method [Instruction] objects, similar to how [Fingerprint] matches entire methods. * * The most basic filters match only opcodes and nothing more, * and more precise filters can match: @@ -113,6 +207,9 @@ fun interface InstructionLocation { * - Method calls (invoke_* opcodes) by name/parameter/return type. * - Object instantiation for specific class types. * - Literal const values. + * + * If creating a custom filter for unusual or app specific purposes, consider extending + * [OpcodeFilter] or [OpcodesFilter] to reduce boilerplate opcode checking logic. */ fun interface InstructionFilter { @@ -138,7 +235,7 @@ fun interface InstructionFilter { class AnyInstruction internal constructor( internal val filters: List, - override val location : InstructionLocation + override val location: InstructionLocation ) : InstructionFilter { override fun matches( @@ -153,17 +250,28 @@ class AnyInstruction internal constructor( /** * Logical OR operator where the first filter that matches satisfies this filter. + * + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun anyInstruction( vararg filters: InstructionFilter, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = AnyInstruction(filters.asList(), location) +/** + * Single opcode match. + * + * Patches can extend this as desired to do unusual or app specific instruction filtering. + * Or Alternatively can implement [InstructionFilter] directly. + * + * @param opcode Opcode to match. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ open class OpcodeFilter( val opcode: Opcode, - override val location : InstructionLocation + override val location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) : InstructionFilter { override fun matches( @@ -175,7 +283,10 @@ open class OpcodeFilter( } /** - * Single opcode. + * Single opcode match. + * + * @param opcode Opcode to match. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun opcode( opcode: Opcode, @@ -186,19 +297,22 @@ fun opcode( /** * Matches a single instruction from many kinds of opcodes. - * If matching only a single opcode instead use [OpcodeFilter]. + * + * Patches can extend this as desired to do unusual or app specific instruction filtering. + * Or Alternatively can implement [InstructionFilter] directly. + * + * @param opcodes Set of opcodes to match to. Value of `null` will match any opcode. + * If matching only a single opcode then instead use [OpcodeFilter]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ -open class OpcodesFilter private constructor( +open class OpcodesFilter protected constructor( val opcodes: EnumSet?, - override val location : InstructionLocation + override val location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) : InstructionFilter { protected constructor( - /** - * Value of `null` will match any opcode. - */ opcodes: List?, - location : InstructionLocation + location: InstructionLocation ) : this(if (opcodes == null) null else EnumSet.copyOf(opcodes), location) override fun matches( @@ -211,7 +325,7 @@ open class OpcodesFilter private constructor( return opcodes.contains(instruction.opcode) } - companion object { + internal companion object { /** * First opcode can match anywhere in a method, but all * subsequent opcodes must match after the previous opcode. @@ -249,12 +363,15 @@ open class OpcodesFilter private constructor( class LiteralFilter internal constructor( - var literal: () -> Long, + val literal: () -> Long, opcodes: List? = null, - location : InstructionLocation + location: InstructionLocation ) : OpcodesFilter(opcodes, location) { - private var literalValue: Long? = null + /** + * Store the lambda value instead of calling it more than once. + */ + private val literalValue: Long by lazy(literal) override fun matches( enclosingMethod: Method, @@ -266,141 +383,89 @@ class LiteralFilter internal constructor( if (instruction !is WideLiteralInstruction) return false - if (literalValue == null) { - literalValue = literal() - } - return instruction.wideLiteral == literalValue } } /** - * Literal value, such as: - * `const v1, 0x7f080318` + * Long literal. Automatically converts literal to opcode hex. * - * that can be matched using: - * `LiteralFilter(0x7f080318)` - * or - * `LiteralFilter(2131231512)` - */ -fun literal( - literal: () -> Long, - opcodes: List? = null, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) = LiteralFilter(literal, opcodes, location) - -/** - * Literal value, such as: - * `const v1, 0x7f080318` - * - * that can be matched using: - * `LiteralFilter(0x7f080318)` - * or - * `LiteralFilter(2131231512L)` + * @param literal Literal number. + * @param opcodes Opcodes to match. By default this matches any literal number opcode such as: + * [Opcode.CONST_4], [Opcode.CONST_16], [Opcode.CONST], [Opcode.CONST_WIDE]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun literal( literal: Long, opcodes: List? = null, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = LiteralFilter({ literal }, opcodes, location) /** - * Integer point literal. + * Integer literal. Automatically converts literal to opcode hex. + * + * @param literal Literal number. + * @param opcodes Opcodes to match. By default this matches any literal number opcode such as: + * [Opcode.CONST_4], [Opcode.CONST_16], [Opcode.CONST], [Opcode.CONST_WIDE]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun literal( literal: Int, opcodes: List? = null, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = LiteralFilter({ literal.toLong() }, opcodes, location) /** - * Double point literal. + * Double point literal. Automatically converts literal to opcode hex. + * + * @param literal Literal number. + * @param opcodes Opcodes to match. By default this matches any literal number opcode such as: + * [Opcode.CONST_4], [Opcode.CONST_16], [Opcode.CONST], [Opcode.CONST_WIDE]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun literal( literal: Double, opcodes: List? = null, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = LiteralFilter({ literal.toRawBits() }, opcodes, location) /** - * Floating point literal. + * Floating point literal. Automatically converts literal to opcode hex. + * + * @param literal Floating point literal. + * @param opcodes Opcodes to match. By default this matches any literal number opcode such as: + * [Opcode.CONST_4], [Opcode.CONST_16], [Opcode.CONST], [Opcode.CONST_WIDE]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun literal( literal: Float, opcodes: List? = null, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = LiteralFilter({ literal.toRawBits().toLong() }, opcodes, location) - - -enum class StringMatchType { - EQUALS, - CONTAINS, - STARTS_WITH, - ENDS_WITH -} - -class StringFilter internal constructor( - var string: () -> String, - var matchType: StringMatchType, - location : InstructionLocation -) : OpcodesFilter(listOf(Opcode.CONST_STRING, Opcode.CONST_STRING_JUMBO), location) { - - override fun matches( - enclosingMethod: Method, - instruction: Instruction - ): Boolean { - if (!super.matches(enclosingMethod, instruction)) { - return false - } - - val instructionString = ((instruction as ReferenceInstruction).reference as StringReference).string - val filterString = string() - - return when (matchType) { - StringMatchType.EQUALS -> instructionString == filterString - StringMatchType.CONTAINS -> instructionString.contains(filterString) - StringMatchType.STARTS_WITH -> instructionString.startsWith(filterString) - StringMatchType.ENDS_WITH -> instructionString.endsWith(filterString) - } - } -} - /** - * Literal String instruction. + * Literal number value. Automatically converts the provided number to opcode hex. + * + * @param literal Literal number. + * @param opcodes Opcodes to match. By default this matches any literal number opcode such as: + * [Opcode.CONST_4], [Opcode.CONST_16], [Opcode.CONST], [Opcode.CONST_WIDE]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ -fun string( - string: () -> String, - /** - * If [string] is a partial match, where the target string contains this string. - * For more precise matching, consider using [anyInstruction] with multiple exact string declarations. - */ - matchType: StringMatchType = StringMatchType.EQUALS, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) = StringFilter(string, matchType, location) - -/** - * Literal String instruction. - */ -fun string( - string: String, - /** - * How to compare [string] against the string constant opcode. For more precise matching - * of multiple strings, consider using [anyInstruction] with multiple exact string declarations. - */ - matchType: StringMatchType = StringMatchType.EQUALS, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) = StringFilter({ string }, matchType, location) +fun literal( + literal: () -> Long, + opcodes: List? = null, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = LiteralFilter(literal, opcodes, location) class MethodCallFilter internal constructor( - val definingClass: (() -> String)? = null, - val name: (() -> String)? = null, - val parameters: (() -> List)? = null, - val returnType: (() -> String)? = null, + val definingClass: String? = null, + val name: String? = null, + val parameters: List? = null, + val returnType: String? = null, opcodes: List? = null, - location : InstructionLocation + location: InstructionLocation ) : OpcodesFilter(opcodes, location) { override fun matches( @@ -412,13 +477,12 @@ class MethodCallFilter internal constructor( } val reference = (instruction as? ReferenceInstruction)?.reference as? MethodReference - if (reference == null) return false + ?: return false if (definingClass != null) { val referenceClass = reference.definingClass - val definingClass = definingClass() - if (!referenceClass.endsWith(definingClass)) { + if (!StringComparisonType.ENDS_WITH.compare(referenceClass, definingClass)) { // Check if 'this' defining class is used. // Would be nice if this also checked all super classes, // but doing so requires iteratively checking all superclasses @@ -428,26 +492,31 @@ class MethodCallFilter internal constructor( } // else, the method call is for 'this' class. } } - if (name != null && reference.name != name()) { + + if (name != null && reference.name != name) { return false } - if (returnType != null && !reference.returnType.startsWith(returnType())) { + + if (returnType != null && + !StringComparisonType.STARTS_WITH.compare(reference.returnType, returnType)) { return false } - if (parameters != null && !parametersStartsWith(reference.parameterTypes, parameters())) { + + if (parameters != null && + !parametersStartsWith(reference.parameterTypes, parameters)) { return false } return true } - companion object { + internal companion object { private val regex = Regex("""^(L[^;]+;)->([^(\s]+)\(([^)]*)\)(\[?L[^;]+;|\[?[BCSIJFDZV])${'$'}""") internal fun parseJvmMethodCall( methodSignature: String, opcodes: List? = null, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ): MethodCallFilter { val matchResult = regex.matchEntire(methodSignature) ?: throw IllegalArgumentException("Invalid method signature: $methodSignature") @@ -460,10 +529,10 @@ class MethodCallFilter internal constructor( val paramDescriptors = parseParameterDescriptors(paramDescriptorString) return MethodCallFilter( - { classDescriptor }, - { methodName }, - { paramDescriptors }, - { returnDescriptor }, + classDescriptor, + methodName, + paramDescriptors, + returnDescriptor, opcodes, location ) @@ -519,46 +588,28 @@ class MethodCallFilter internal constructor( } /** - * Identifies method calls. + * Matches a method call, such as: + * `invoke-virtual {v3, v4}, La;->b(I)V` * - * `Null` parameters matches anything. - * - * By default any type of method call matches. - * Specify opcodes if a specific type of method call is desired (such as only static calls). + * @param definingClass Defining class of the field call. Compares using [StringComparisonType.ENDS_WITH]. + * For calls to a method in the same class, use 'this' as the defining class. + * Note: 'this' does not work for fields found in superclasses. + * @param name Full name of the method. Compares using [StringComparisonType.EQUALS]. + * @param parameters Parameters of the method call. Each parameter matches using[StringComparisonType.STARTS_WITH] + * and semantics are the same as [Fingerprint] parameters. + * @param returnType Return type. Matches using [StringComparisonType.STARTS_WITH]. + * @param opcodes Opcode types to match. By default this matches any method call opcode: `Opcode.INVOKE_*`. + * If this filter must match specific types of method call, then specify the desired opcodes +such as [Opcode.INVOKE_STATIC], [Opcode.INVOKE_STATIC_RANGE] to match only static calls. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun methodCall( - /** - * Defining class of the method call. Matches using endsWith(). - * - * For calls to a method in the same class, use 'this' as the defining class. - * Note: 'this' does not work for methods declared only in a superclass. - */ - definingClass: (() -> String)? = null, - /** - * Method name. Must be exact match of the method name. - */ - name: (() -> String)? = null, - /** - * Parameters of the method call. Each parameter matches - * using startsWith() and semantics are the same as [Fingerprint]. - */ - parameters: (() -> List)? = null, - /** - * Return type. Matches using startsWith() - */ - returnType: (() -> String)? = null, - /** - * Opcode types to match. By default this matches any method call opcode: - * `Opcode.INVOKE_*`. - * - * If this filter must match specific types of method call, then specify the desired opcodes - * such as [Opcode.INVOKE_STATIC], [Opcode.INVOKE_STATIC_RANGE] to match only static calls. - */ + definingClass: String? = null, + name: String? = null, + parameters: List? = null, + returnType: String? = null, opcodes: List? = null, - /** - * The locations where this filter is allowed to match. - */ - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = MethodCallFilter( definingClass, name, @@ -568,102 +619,32 @@ fun methodCall( location ) +/** + * Matches a method call, such as: + * `invoke-virtual {v3, v4}, La;->b(I)V` + * + * @param definingClass Defining class of the field call. Compares using [StringComparisonType.ENDS_WITH]. + * For calls to a method in the same class, use 'this' as the defining class. + * Note: 'this' does not work for fields found in superclasses. + * @param name Full name of the method. Compares using [StringComparisonType.EQUALS]. + * @param parameters Parameters of the method call. Each parameter matches using[StringComparisonType.STARTS_WITH] + * and semantics are the same as [Fingerprint] parameters. + * @param returnType Return type. Matches using [StringComparisonType.STARTS_WITH]. + * @param opcode Single opcode type to match. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ fun methodCall( - /** - * Defining class of the method call. Matches using endsWith(). - * - * For calls to a method in the same class, use 'this' as the defining class. - * Note: 'this' does not work for methods declared only in a superclass. - */ definingClass: String? = null, - /** - * Method name. Must be exact match of the method name. - */ name: String? = null, - /** - * Parameters of the method call. Each parameter matches - * using startsWith() and semantics are the same as [Fingerprint]. - */ parameters: List? = null, - /** - * Return type. Matches using startsWith() - */ returnType: String? = null, - /** - * Opcode types to match. By default this matches any method call opcode: - * `Opcode.INVOKE_*`. - * - * If this filter must match specific types of method call, then specify the desired opcodes - * such as [Opcode.INVOKE_STATIC], [Opcode.INVOKE_STATIC_RANGE] to match only static calls. - */ - opcodes: List? = null, - /** - * The locations where this filter is allowed to match. - */ - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) = MethodCallFilter( - if (definingClass != null) { - { definingClass } - } else null, - if (name != null) { - { name } - } else null, - if (parameters != null) { - { parameters } - } else null, - if (returnType != null) { - { returnType } - } else null, - opcodes, - location -) - -fun methodCall( - /** - * Defining class of the method call. Matches using endsWith(). - * - * For calls to a method in the same class, use 'this' as the defining class. - * Note: 'this' does not work for methods declared only in a superclass. - */ - definingClass: String? = null, - /** - * Method name. Must be exact match of the method name. - */ - name: String? = null, - /** - * Parameters of the method call. Each parameter matches - * using startsWith() and semantics are the same as [Fingerprint]. - */ - parameters: List? = null, - /** - * Return type. Matches using startsWith() - */ - returnType: String? = null, - /** - * Opcode types to match. By default this matches any method call opcode: - * `Opcode.INVOKE_*`. - * - * If this filter must match specific types of method call, then specify the desired opcodes - * such as [Opcode.INVOKE_STATIC], [Opcode.INVOKE_STATIC_RANGE] to match only static calls. - */ opcode: Opcode, - /** - * The locations where this filter is allowed to match. - */ - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = MethodCallFilter( - if (definingClass != null) { - { definingClass } - } else null, - if (name != null) { - { name } - } else null, - if (parameters != null) { - { parameters } - } else null, - if (returnType != null) { - { returnType } - } else null, + definingClass, + name, + parameters, + returnType, listOf(opcode), location ) @@ -672,34 +653,44 @@ fun methodCall( * Method call for a copy pasted SMALI style method signature. e.g.: * `Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;` * - * Does not support obfuscated method names or parameter/return types. + * Should never be used with obfuscated method names or parameter/return types. + * + * @param smali Smali method call reference, such as + * `Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;`. + * @param opcodes List of all possible opcodes to match. Defaults to matching all method calls types: `Opcode.INVOKE_*`. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun methodCall( smali: String, opcodes: List? = null, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = parseJvmMethodCall(smali, opcodes, location) /** * Method call for a copy pasted SMALI style method signature. e.g.: * `Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;` * - * Does not support obfuscated method names or parameter/return types. + * Should never be used with obfuscated method names or parameter/return types. + * + * @param smali Smali method call reference, such as + * `Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;`. + * @param opcode Single opcode type to match. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun methodCall( smali: String, opcode: Opcode, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = parseJvmMethodCall(smali, listOf(opcode), location) class FieldAccessFilter internal constructor( - val definingClass: (() -> String)? = null, - val name: (() -> String)? = null, - val type: (() -> String)? = null, + val definingClass: String? = null, + val name: String? = null, + val type: String? = null, opcodes: List? = null, - location : InstructionLocation + location: InstructionLocation ) : OpcodesFilter(opcodes, location) { override fun matches( @@ -711,11 +702,10 @@ class FieldAccessFilter internal constructor( } val reference = (instruction as? ReferenceInstruction)?.reference as? FieldReference - if (reference == null) return false + ?: return false if (definingClass != null) { val referenceClass = reference.definingClass - val definingClass = definingClass() if (!referenceClass.endsWith(definingClass)) { if (!(definingClass == "this" && referenceClass == enclosingMethod.definingClass)) { @@ -723,10 +713,12 @@ class FieldAccessFilter internal constructor( } // else, the method call is for 'this' class. } } - if (name != null && reference.name != name()) { + + if (name != null && reference.name != name) { return false } - if (type != null && !reference.type.startsWith(type())) { + + if (type != null && !reference.type.startsWith(type)) { return false } @@ -739,7 +731,7 @@ class FieldAccessFilter internal constructor( internal fun parseJvmFieldAccess( fieldSignature: String, opcodes: List? = null, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ): FieldAccessFilter { val matchResult = regex.matchEntire(fieldSignature) ?: throw IllegalArgumentException("Invalid field access smali: $fieldSignature") @@ -755,107 +747,25 @@ class FieldAccessFilter internal constructor( } } -/** - * Matches a field call, such as: - * `iget-object v0, p0, Lahhh;->g:Landroid/view/View;` - */ -fun fieldAccess( - /** - * Defining class of the field call. Matches using endsWith(). - * - * For calls to a method in the same class, use 'this' as the defining class. - * Note: 'this' does not work for fields found in superclasses. - */ - definingClass: (() -> String)? = null, - /** - * Name of the field. Must be a full match of the field name. - */ - name: (() -> String)? = null, - /** - * Class type of field. Partial matches using startsWith() is allowed. - */ - type: (() -> String)? = null, - /** - * Valid opcodes matches for this instruction. - * By default this matches any kind of field access - * (`Opcode.IGET`, `Opcode.SGET`, `Opcode.IPUT`, `Opcode.SPUT`, etc). - */ - opcodes: List? = null, - /** - * The locations where this filter is allowed to match. - */ - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) = FieldAccessFilter(definingClass, name, type, opcodes, location) /** * Matches a field call, such as: * `iget-object v0, p0, Lahhh;->g:Landroid/view/View;` + * + * @param definingClass Defining class of the field call. Compares using [StringComparisonType.ENDS_WITH]. + * For calls to a method in the same class, use 'this' as the defining class. + * Note: 'this' does not work for fields found in superclasses. + * @param name Full name of the field. Compares using [StringComparisonType.EQUALS]. + * @param type Class type of field. Compares using [StringComparisonType.STARTS_WITH]. + * @param opcode Single opcode type to match. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun fieldAccess( - /** - * Defining class of the field call. Matches using endsWith(). - * - * For calls to a method in the same class, use 'this' as the defining class. - * Note: 'this' does not work for fields found in superclasses. - */ definingClass: String? = null, - /** - * Name of the field. Must be a full match of the field name. - */ name: String? = null, - /** - * Class type of field. Partial matches using startsWith() is allowed. - */ - type: String? = null, - /** - * Valid opcodes matches for this instruction. - * By default this matches any kind of field access - * (`Opcode.IGET`, `Opcode.SGET`, `Opcode.IPUT`, `Opcode.SPUT`, etc). - */ - opcodes: List? = null, - /** - * The locations where this filter is allowed to match. - */ - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) = FieldAccessFilter( - if (definingClass != null) { - { definingClass } - } else null, - if (name != null) { - { name } - } else null, - if (type != null) { - { type } - } else null, - opcodes, - location -) - -/** - * Matches a field call, such as: - * `iget-object v0, p0, Lahhh;->g:Landroid/view/View;` - */ -fun fieldAccess( - /** - * Defining class of the field call. Matches using endsWith(). - * - * For calls to a method in the same class, use 'this' as the defining class. - * Note: 'this' does not work for fields found in superclasses. - */ - definingClass: String? = null, - /** - * Name of the field. Must be a full match of the field name. - */ - name: String? = null, - /** - * Class type of field. Partial matches using startsWith() is allowed. - */ type: String? = null, opcode: Opcode, - /** - * The locations where this filter is allowed to match. - */ - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = fieldAccess( definingClass, name, @@ -864,36 +774,77 @@ fun fieldAccess( location ) +/** + * Matches a field call, such as: + * `iget-object v0, p0, Lahhh;->g:Landroid/view/View;` + * + * @param definingClass Defining class of the field call. Compares using [StringComparisonType.ENDS_WITH]. + * For calls to a method in the same class, use 'this' as the defining class. + * Note: 'this' does not work for fields found in superclasses. + * @param name Full name of the field. Compares using [StringComparisonType.EQUALS]. + * @param type Class type of field. Compares using [StringComparisonType.STARTS_WITH]. + * @param opcodes List of all possible opcodes to match. Defaults to matching all get/put opcodes. + * (`Opcode.IGET`, `Opcode.SGET`, `Opcode.IPUT`, `Opcode.SPUT`, etc). + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun fieldAccess( + definingClass: String? = null, + name: String? = null, + type: String? = null, + opcodes: List? = null, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = FieldAccessFilter( + definingClass, + name, + type, + opcodes, + location +) + /** * Field access for a copy pasted SMALI style field access call. e.g.: * `Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;` * - * Does not support obfuscated field names or obfuscated field types. + * Should never be used with obfuscated field names or obfuscated field types. + * @param smali Smali field access statement, such as `Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;`. + * @param opcodes List of all possible opcodes to match. Defaults to matching all get/put opcodes. + * (`Opcode.IGET`, `Opcode.SGET`, `Opcode.IPUT`, `Opcode.SPUT`, etc). + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun fieldAccess( smali: String, opcodes: List? = null, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = parseJvmFieldAccess(smali, opcodes, location) /** * Field access for a copy pasted SMALI style field access call. e.g.: * `Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;` * - * Does not support obfuscated field names or obfuscated field types. + * Should never be used with obfuscated field names or obfuscated field types. + * + * @param smali Smali field access statement, such as `Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;`. + * @param opcode Single opcode type to match. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun fieldAccess( smali: String, opcode: Opcode, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) = parseJvmFieldAccess(smali, listOf(opcode), location) -class NewInstanceFilter internal constructor ( - var type: () -> String, - location : InstructionLocation -) : OpcodesFilter(listOf(Opcode.NEW_INSTANCE, Opcode.NEW_ARRAY), location) { +class StringFilter internal constructor( + val string: () -> String, + val comparison: StringComparisonType, + location: InstructionLocation +) : OpcodesFilter(listOf(Opcode.CONST_STRING, Opcode.CONST_STRING_JUMBO), location) { + + /** + * Store the lambda value instead of calling it more than once. + */ + private val stringValue: String by lazy (string) override fun matches( enclosingMethod: Method, @@ -903,44 +854,163 @@ class NewInstanceFilter internal constructor ( return false } - val reference = (instruction as? ReferenceInstruction)?.reference as? TypeReference - if (reference == null) return false - - return reference.type.endsWith(type()) + val stringReference = (instruction as ReferenceInstruction).reference as StringReference + return comparison.compare(stringReference.string, stringValue) } } - /** - * Opcode type [Opcode.NEW_INSTANCE] or [Opcode.NEW_ARRAY] with a non obfuscated class type. + * Literal String instruction. * - * @param type Class type that matches the target instruction using [String.endsWith]. + * @param string string literal, using exact matching of [StringComparisonType.EQUALS]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ -fun newInstancetype( - type: () -> String, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) = NewInstanceFilter(type, location) +fun string( + string: String, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = StringFilter({ string }, StringComparisonType.EQUALS, location) + +/** + * Literal String instruction. + * + * @param string string literal, using exact matching of [StringComparisonType.EQUALS]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun string( + string: () -> String, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = StringFilter(string, StringComparisonType.EQUALS, location) + +/** + * Literal String instruction. + * + * @param string string literal. + * @param comparison How to compare the string literal. For more precise matching of strings, + * consider using [anyInstruction] with multiple exact string declarations. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun string( + string: String, + /** + * How to match a given string opcode literal. Default is exact string equality. For more + * precise matching of multiple strings, consider using [anyInstruction] with multiple + * exact string declarations. + */ + comparison: StringComparisonType = StringComparisonType.EQUALS, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = StringFilter({ string }, comparison, location) + +/** + * Literal String instruction. + * + * @param string string literal. + * @param comparison How to compare the string literal. For more precise matching of strings, + * consider using [anyInstruction] with multiple exact string declarations. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun string( + string: () -> String, + comparison: StringComparisonType, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = StringFilter(string, comparison, location) + + + +class NewInstanceFilter internal constructor ( + val type: () -> String, + val comparison: StringComparisonType, + location: InstructionLocation +) : OpcodesFilter(listOf(Opcode.NEW_INSTANCE, Opcode.NEW_ARRAY), location) { + + /** + * Store the lambda value instead of calling it more than once. + */ + private val typeValue: String by lazy { + val typeValue = type() + comparison.validateSearchStringForClassType(typeValue) + typeValue + } + + override fun matches( + enclosingMethod: Method, + instruction: Instruction + ): Boolean { + if (!super.matches(enclosingMethod, instruction)) { + return false + } + + val reference = (instruction as ReferenceInstruction).reference as TypeReference + return comparison.compare(reference.type, typeValue) + } +} /** * Opcode type [Opcode.NEW_INSTANCE] or [Opcode.NEW_ARRAY] with a non obfuscated class type. * - * @param type Class type that matches the target instruction using [String.endsWith]. + * @param type Class type, compared using [StringComparisonType.ENDS_WITH]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun newInstance( type: String, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) : NewInstanceFilter { - if (!type.endsWith(";")) { - throw IllegalArgumentException("Class type does not end with a semicolon: $type") - } - return NewInstanceFilter({ type }, location) -} + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = NewInstanceFilter({ type }, StringComparisonType.ENDS_WITH, location) -class CheckCastFilter internal constructor ( - var type: () -> String, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() +/** + * Opcode type [Opcode.NEW_INSTANCE] or [Opcode.NEW_ARRAY] with a non obfuscated class type. + * + * @param type Class type, compared using [StringComparisonType.ENDS_WITH]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun newInstance( + type: () -> String, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere(), +) = NewInstanceFilter(type, StringComparisonType.ENDS_WITH, location) + +/** + * Opcode type [Opcode.NEW_INSTANCE] or [Opcode.NEW_ARRAY] with a non obfuscated class type. + * + * @param type Class type. + * @param comparison How to compare the opcode class type. For more precise matching of types, + * consider using [anyInstruction] with multiple exact type declarations. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun newInstance( + type: String, + comparison: StringComparisonType, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = NewInstanceFilter({ type }, comparison, location) + +/** + * Opcode type [Opcode.NEW_INSTANCE] or [Opcode.NEW_ARRAY] with a non obfuscated class type. + * + * @param type Class type. + * @param comparison How to compare the opcode class type. For more precise matching of types, + * consider using [anyInstruction] with multiple exact type declarations. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun newInstance( + type: () -> String, + comparison: StringComparisonType, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = NewInstanceFilter(type, comparison, location) + + + +class CheckCastFilter internal constructor( + val type: () -> String, + val comparison: StringComparisonType, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() ) : OpcodeFilter(Opcode.CHECK_CAST, location) { + /** + * Store the lambda value instead of calling it more than once. + */ + private val typeValue: String by lazy { + val typeValue = type() + comparison.validateSearchStringForClassType(typeValue) + typeValue + } + override fun matches( enclosingMethod: Method, instruction: Instruction @@ -949,35 +1019,57 @@ class CheckCastFilter internal constructor ( return false } - val reference = (instruction as? ReferenceInstruction)?.reference as? TypeReference - if (reference == null) return false - - return reference.type.endsWith(type()) + val reference = (instruction as ReferenceInstruction).reference as TypeReference + return comparison.compare(reference.type, typeValue) } } /** * Opcode type [Opcode.CHECK_CAST] with a non obfuscated class type. * - * @param type Class type that matches the target instruction using [String.endsWith]. - */ -fun checkCast( - type: () -> String, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) = CheckCastFilter(type, location) - -/** - * Opcode type [Opcode.CHECK_CAST] with a non obfuscated class type. - * - * @param type Class type that matches the target instruction using [String.endsWith]. + * @param type Class type, compared using [StringComparisonType.ENDS_WITH]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. */ fun checkCast( type: String, - location : InstructionLocation = InstructionLocation.MatchAfterAnywhere() -) : CheckCastFilter { - if (!type.endsWith(";")) { - throw IllegalArgumentException("Class type does not end with a semicolon: $type") - } + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = CheckCastFilter({ type }, StringComparisonType.ENDS_WITH, location) - return CheckCastFilter({ type }, location) -} +/** + * Opcode type [Opcode.CHECK_CAST] with a non obfuscated class type. + * + * @param type Class type, compared using [StringComparisonType.ENDS_WITH]. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun checkCast( + type: () -> String, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = CheckCastFilter(type, StringComparisonType.ENDS_WITH, location) + +/** + * Opcode type [Opcode.CHECK_CAST] with a non obfuscated class type using the provided string comparison type. + * + * @param type Class type. + * @param comparison How to compare the opcode class type. For more precise matching of types, + * consider using [anyInstruction] with multiple exact type declarations. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun checkCast( + type: String, + comparison: StringComparisonType, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = CheckCastFilter({ type }, comparison, location) + +/** + * Opcode type [Opcode.CHECK_CAST] with a non obfuscated class type using the provided string comparison type. + * + * @param type Class type. + * @param comparison How to compare the opcode class type. For more precise matching of types, + * consider using [anyInstruction] with multiple exact type declarations. + * @param location Where this filter is allowed to match. Default is anywhere after the previous instruction. + */ +fun checkCast( + type: () -> String, + comparison: StringComparisonType, + location: InstructionLocation = InstructionLocation.MatchAfterAnywhere() +) = CheckCastFilter(type, comparison, location) diff --git a/src/main/kotlin/app/revanced/patcher/extensions/Method.kt b/src/main/kotlin/app/revanced/patcher/extensions/Method.kt index 6149e90..cc26883 100644 --- a/src/main/kotlin/app/revanced/patcher/extensions/Method.kt +++ b/src/main/kotlin/app/revanced/patcher/extensions/Method.kt @@ -73,6 +73,42 @@ fun MutableMethodImplementation.replaceInstructions( addInstructions(index, instructions) } +/** + * Add an instruction to a method at the given index. + * + * @param index The index to add the instruction at. + * @param instruction The instruction to add. + */ +fun MutableMethod.addInstruction( + index: Int, + instruction: BuilderInstruction, +) = implementation!!.addInstruction(index, instruction) + +/** + * Add an instruction to a method. + * + * @param instruction The instructions to add. + */ +fun MutableMethod.addInstruction(instruction: BuilderInstruction) = implementation!!.addInstruction(instruction) + +/** + * Add an instruction to a method at the given index. + * + * @param index The index to add the instruction at. + * @param smaliInstructions The instruction to add. + */ +fun MutableMethod.addInstruction( + index: Int, + smaliInstructions: String, +) = implementation!!.addInstructions(index, smaliInstructions.toInstructions(this)) + +/** + * Add an instruction to a method. + * + * @param smaliInstructions The instruction to add. + */ +fun MutableMethod.addInstruction(smaliInstructions: String) = implementation!!.addInstructions(smaliInstructions.toInstructions(this)) + /** * Add instructions to a method at the given index. * @@ -89,8 +125,7 @@ fun MutableMethod.addInstructions( * * @param instructions The instructions to add. */ -fun MutableMethod.addInstructions(instructions: List) = - implementation!!.addInstructions(instructions) +fun MutableMethod.addInstructions(instructions: List) = implementation!!.addInstructions(instructions) /** * Add instructions to a method. @@ -107,8 +142,7 @@ fun MutableMethod.addInstructions( * * @param smaliInstructions The instructions to add. */ -fun MutableMethod.addInstructions(smaliInstructions: String) = - implementation!!.addInstructions(smaliInstructions.toInstructions(this)) +fun MutableMethod.addInstructions(smaliInstructions: String) = implementation!!.addInstructions(smaliInstructions.toInstructions(this)) /** * Add instructions to a method at the given index. @@ -166,7 +200,6 @@ fun MutableMethod.addInstructionsWithLabels( i.registerB, label, ) - is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label) is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label) else -> throw IllegalStateException( @@ -211,6 +244,13 @@ fun MutableMethod.addInstructionsWithLabels( } } +/** + * Remove an instruction at the given index. + * + * @param index The index to remove the instruction at. + */ +fun MutableMethod.removeInstruction(index: Int) = implementation!!.removeInstruction(index) + /** * Remove instructions at the given index. * @@ -229,6 +269,28 @@ fun MutableMethod.removeInstructions( */ fun MutableMethod.removeInstructions(count: Int) = implementation!!.removeInstructions(count) +/** + * Replace an instruction at the given index. + * + * @param index The index to replace the instruction at. + * @param instruction The instruction to replace the instruction with. + */ +fun MutableMethod.replaceInstruction( + index: Int, + instruction: BuilderInstruction, +) = implementation!!.replaceInstruction(index, instruction) + +/** + * Replace an instruction at the given index. + * + * @param index The index to replace the instruction at. + * @param smaliInstruction The smali instruction to replace the instruction with. + */ +fun MutableMethod.replaceInstruction( + index: Int, + smaliInstruction: String, +) = implementation!!.replaceInstructions(index, smaliInstruction.toInstructions(this)) + /** * Replace instructions at the given index. * diff --git a/src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt b/src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt index 7fb5ee0..4021b9c 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt @@ -21,6 +21,7 @@ import lanchon.multidexlib2.MultiDexIO import lanchon.multidexlib2.RawDexIO import java.io.Closeable import java.io.IOException +import java.util.LinkedHashMap import java.util.logging.Logger @@ -140,8 +141,12 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi this, BasicDexFileNamer(), object : DexFile { - override fun getClasses() = - this@BytecodePatchContext.classDefs.toSet() + override fun getClasses() = this@BytecodePatchContext.classDefs.let { + // More performant according to + // https://github.com/LisoUseInAIKyrios/revanced-patcher/commit/8c26ad08457fb1565ea5794b7930da42a1c81cf1#diff-be698366d9868784ecf7da3fd4ac9d2b335b0bb637f9f618fbe067dbd6830b8fR197 + // TODO: Benchmark, if actually faster. + HashSet(it.size * 3 / 2).apply { addAll(it) } + } override fun getOpcodes() = this@BytecodePatchContext.opcodes }, @@ -166,10 +171,17 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi } internal inner class LookupMaps { + // No custom HashMap needed here, according to + // https://github.com/LisoUseInAIKyrios/revanced-patcher/commit/9b6d95d4f414a35ed68da37b0ecd8549df1ef63a + // TODO: Benchmark, if actually faster. private val _classDefsByType = mutableMapOf() val classDefsByType: Map = _classDefsByType - private val _methodsByStrings = mutableMapOf>() + // Better performance according to + // https://github.com/LisoUseInAIKyrios/revanced-patcher/commit/9b6d95d4f414a35ed68da37b0ecd8549df1ef63a + private val _methodsByStrings = + LinkedHashMap>(2 * classDefs.size, 0.5f) + val methodsByStrings: Map> = _methodsByStrings private val _methodsWithString = methodsByStrings.values.flatten().toMutableSet() @@ -195,7 +207,9 @@ class BytecodePatchContext internal constructor(private val config: PatcherConfi classDef.forEachString { method, string -> _methodsWithString += method - _methodsByStrings.getOrPut(string) { mutableListOf() } += method + _methodsByStrings.getOrPut(string) { + mutableListOf() + } += method } } diff --git a/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt b/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt index 0a1ee25..728bce9 100644 --- a/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt +++ b/src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt @@ -1,5 +1,6 @@ package app.revanced.patcher.util +import app.revanced.patcher.dex.mutable.MutableClassDef import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.util.ClassMerger.Utils.asMutableClass import app.revanced.patcher.util.ClassMerger.Utils.filterAny @@ -7,7 +8,6 @@ import app.revanced.patcher.util.ClassMerger.Utils.filterNotAny import app.revanced.patcher.util.ClassMerger.Utils.isPublic import app.revanced.patcher.util.ClassMerger.Utils.toPublic import app.revanced.patcher.util.ClassMerger.Utils.traverseClassHierarchy -import app.revanced.patcher.dex.mutable.MutableClassDef import app.revanced.patcher.dex.mutable.MutableClassDef.Companion.toMutable import app.revanced.patcher.dex.mutable.MutableField import app.revanced.patcher.dex.mutable.MutableField.Companion.toMutable diff --git a/src/test/kotlin/app/revanced/patcher/extensions/InstructionExtensionsTest.kt b/src/test/kotlin/app/revanced/patcher/extensions/InstructionExtensionsTest.kt index c8c9382..b415b47 100644 --- a/src/test/kotlin/app/revanced/patcher/extensions/InstructionExtensionsTest.kt +++ b/src/test/kotlin/app/revanced/patcher/extensions/InstructionExtensionsTest.kt @@ -2,7 +2,6 @@ package app.revanced.patcher.extensions import app.revanced.patcher.dex.mutable.MutableMethod import app.revanced.patcher.dex.mutable.MutableMethod.Companion.toMutable -import app.revanced.patcher.util.smali.ExternalLabel import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.BuilderOffsetInstruction