From 4a897fdb981e041c4d1d087e9416a0d332bd0cc4 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:17:00 +0200 Subject: [PATCH] refactor: Add more opcodes to findFreeRegister --- .../hide/general/HideLayoutComponentsPatch.kt | 2 +- .../ReturnYouTubeDislikePatch.kt | 3 - .../kotlin/app/revanced/util/BytecodeUtils.kt | 72 +++++++++++++++---- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index 6b7889bb2..e7e58bc2a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -269,7 +269,7 @@ val hideLayoutComponentsPatch = bytecodePatch( move-object v$returnEmptyComponentRegister, p1 # Required for 19.47 goto :return_empty_component :show - const/4 v$freeRegister, 0x0 # Restore register, required for 19.16 + nop """, ExternalLabel("return_empty_component", returnEmptyComponentInstruction), ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt index 02d9aa46d..9eca261d1 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt @@ -179,9 +179,6 @@ val returnYouTubeDislikePatch = bytecodePatch( // region Hook rolling numbers. - // Do this last to allow patching old unsupported versions (if the user really wants), - // On older unsupported version this will fail to match and throw an exception, - // but everything will still work correctly anyway. val dislikesIndex = rollingNumberSetterFingerprint.patternMatch!!.endIndex rollingNumberSetterFingerprint.method.apply { diff --git a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt index 50c9160ce..c32a0c072 100644 --- a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt +++ b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -70,21 +70,53 @@ internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: } val writeOpcodes = EnumSet.of( + ARRAY_LENGTH, NEW_INSTANCE, NEW_ARRAY, MOVE, MOVE_FROM16, MOVE_16, MOVE_WIDE, MOVE_WIDE_FROM16, MOVE_WIDE_16, MOVE_OBJECT, MOVE_OBJECT_FROM16, MOVE_OBJECT_16, MOVE_RESULT, MOVE_RESULT_WIDE, MOVE_RESULT_OBJECT, MOVE_EXCEPTION, + CONST, CONST_4, CONST_16, CONST_HIGH16, CONST_WIDE_16, CONST_WIDE_32, + CONST_WIDE, CONST_WIDE_HIGH16, CONST_STRING, CONST_STRING_JUMBO, IGET, IGET_WIDE, IGET_OBJECT, IGET_BOOLEAN, IGET_BYTE, IGET_CHAR, IGET_SHORT, + IGET_VOLATILE, IGET_WIDE_VOLATILE, IGET_OBJECT_VOLATILE, SGET, SGET_WIDE, SGET_OBJECT, SGET_BOOLEAN, SGET_BYTE, SGET_CHAR, SGET_SHORT, + SGET_VOLATILE, SGET_WIDE_VOLATILE, SGET_OBJECT_VOLATILE, + AGET, AGET_WIDE, AGET_OBJECT, AGET_BOOLEAN, AGET_BYTE, AGET_CHAR, AGET_SHORT, + // Arithmetic and logical operations. + ADD_DOUBLE_2ADDR, ADD_DOUBLE, ADD_FLOAT_2ADDR, ADD_FLOAT, ADD_INT_2ADDR, + ADD_INT_LIT8, ADD_INT, ADD_LONG_2ADDR, ADD_LONG, ADD_INT_LIT16, + AND_INT_2ADDR, AND_INT_LIT8, AND_INT_LIT16, AND_INT, AND_LONG_2ADDR, AND_LONG, + DIV_DOUBLE_2ADDR, DIV_DOUBLE, DIV_FLOAT_2ADDR, DIV_FLOAT, DIV_INT_2ADDR, + DIV_INT_LIT16, DIV_INT_LIT8, DIV_INT, DIV_LONG_2ADDR, DIV_LONG, + DOUBLE_TO_FLOAT, DOUBLE_TO_INT, DOUBLE_TO_LONG, + FLOAT_TO_DOUBLE, FLOAT_TO_INT, FLOAT_TO_LONG, + INT_TO_BYTE, INT_TO_CHAR, INT_TO_DOUBLE, INT_TO_FLOAT, INT_TO_LONG, INT_TO_SHORT, + LONG_TO_DOUBLE, LONG_TO_FLOAT, LONG_TO_INT, + MUL_DOUBLE_2ADDR, MUL_DOUBLE, MUL_FLOAT_2ADDR, MUL_FLOAT, MUL_INT_2ADDR, + MUL_INT_LIT16, MUL_INT_LIT8, MUL_INT, MUL_LONG_2ADDR, MUL_LONG, + NEG_DOUBLE, NEG_FLOAT, NEG_INT, NEG_LONG, + NOT_INT, NOT_LONG, + OR_INT_2ADDR, OR_INT_LIT16, OR_INT_LIT8, OR_INT, OR_LONG_2ADDR, OR_LONG, + REM_DOUBLE_2ADDR, REM_DOUBLE, REM_FLOAT_2ADDR, REM_FLOAT, REM_INT_2ADDR, + REM_INT_LIT16, REM_INT_LIT8, REM_INT, REM_LONG_2ADDR, REM_LONG, + RSUB_INT_LIT8, RSUB_INT, + SHL_INT_2ADDR, SHL_INT_LIT8, SHL_INT, SHL_LONG_2ADDR, SHL_LONG, + SHR_INT_2ADDR, SHR_INT_LIT8, SHR_INT, SHR_LONG_2ADDR, SHR_LONG, + SUB_DOUBLE_2ADDR, SUB_DOUBLE, SUB_FLOAT_2ADDR, SUB_FLOAT, SUB_INT_2ADDR, + SUB_INT, SUB_LONG_2ADDR, SUB_LONG, + USHR_INT_2ADDR, USHR_INT_LIT8, USHR_INT, USHR_LONG_2ADDR, USHR_LONG, + XOR_INT_2ADDR, XOR_INT_LIT16, XOR_INT_LIT8, XOR_INT, XOR_LONG_2ADDR, XOR_LONG, ) val branchOpcodes = EnumSet.of( GOTO, GOTO_16, GOTO_32, IF_EQ, IF_NE, IF_LT, IF_GE, IF_GT, IF_LE, IF_EQZ, IF_NEZ, IF_LTZ, IF_GEZ, IF_GTZ, IF_LEZ, + PACKED_SWITCH_PAYLOAD, SPARSE_SWITCH_PAYLOAD ) val returnOpcodes = EnumSet.of( - RETURN_VOID, RETURN, RETURN_WIDE, RETURN_OBJECT, + RETURN_VOID, RETURN, RETURN_WIDE, RETURN_OBJECT, RETURN_VOID_NO_BARRIER, + THROW ) // Highest 4-bit register available, exclusive. Ideally return a free register less than this. @@ -94,9 +126,13 @@ internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: for (i in startIndex until instructions.count()) { val instruction = getInstruction(i) + val instructionRegisters = instruction.getRegistersUsed() if (instruction.opcode in returnOpcodes) { - // Method returns. Use lowest register that hasn't been encountered. + // Method returns. + usedRegisters.addAll(instructionRegisters) + + // Use lowest register that hasn't been encountered. val freeRegister = (0 until implementation!!.registerCount).find { it !in usedRegisters } @@ -122,26 +158,32 @@ internal fun Method.findFreeRegister(startIndex: Int, vararg registersToExclude: } if (instruction.opcode in writeOpcodes) { - val freeRegister = instruction.getRegisterWritten() - if (freeRegister !in usedRegisters) { - if (freeRegister < maxRegister4Bits) { - // Found an ideal register. - return freeRegister - } + val writeRegister = instruction.getRegisterWritten() - // Continue searching for a 4-bit register if available. - if (bestFreeRegisterFound == null || freeRegister < bestFreeRegisterFound) { - bestFreeRegisterFound = freeRegister + if (writeRegister !in usedRegisters) { + // Verify the register is only used for write and not also as a parameter. + // If the instruction uses the write register once then it's not also a read register. + if (instructionRegisters.count { register -> register == writeRegister } == 1) { + if (writeRegister < maxRegister4Bits) { + // Found an ideal register. + return writeRegister + } + + // Continue searching for a 4-bit register if available. + if (bestFreeRegisterFound == null || writeRegister < bestFreeRegisterFound) { + bestFreeRegisterFound = writeRegister + } } } } - usedRegisters.addAll(instruction.getRegistersUsed()) + usedRegisters.addAll(instructionRegisters) } - // Cannot be reached since a branch or return statement will - // be encountered before the end of the method. - throw IllegalStateException() + // Some methods can have array payloads at the end of the method after a return statement. + // But in normal usage this cannot be reached since a branch or return statement + // will be encountered before the end of the method. + throw IllegalArgumentException("Start index is outside the range of normal control flow: $startIndex") }