You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-06 16:38:50 +02:00
Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e4b4bacae8 | ||
![]() |
cbc97af155 | ||
![]() |
d5533788e2 | ||
![]() |
5a4ea5cd7d | ||
![]() |
70f3c8b38c | ||
![]() |
6b410a0eea | ||
![]() |
73a013d75b | ||
![]() |
7159f3db4c | ||
![]() |
7d5ecf095c | ||
![]() |
fa015a424d | ||
![]() |
dd7dd38357 | ||
![]() |
22356f2d26 | ||
![]() |
66701f6076 | ||
![]() |
6a6ded084e | ||
![]() |
5887c69bde | ||
![]() |
4102f43b8a | ||
![]() |
5c09ef7837 | ||
![]() |
3e0bf8c863 | ||
![]() |
8f3ecc318c | ||
![]() |
365da96e2b | ||
![]() |
cd68ec4803 | ||
![]() |
35265e029c | ||
![]() |
9f0a09a756 | ||
![]() |
e802141df5 | ||
![]() |
abebc0862c | ||
![]() |
96ef150e89 | ||
![]() |
c5de9e2988 | ||
![]() |
c391ca648b | ||
![]() |
7cf79e68e0 | ||
![]() |
f07db3c214 | ||
![]() |
88bb3a8845 | ||
![]() |
b9e6bd6775 | ||
![]() |
cd1b72e078 | ||
![]() |
6b889557ab | ||
![]() |
4b1be8c647 | ||
![]() |
73c893c6e7 | ||
![]() |
75b36823b8 | ||
![]() |
d2d93cd075 | ||
![]() |
26b8621ac8 | ||
![]() |
f365a41741 | ||
![]() |
9ec720e983 | ||
![]() |
0f432b3fdd | ||
![]() |
96cd5618dd | ||
![]() |
c2a5a55e67 | ||
![]() |
6c5de8b414 | ||
![]() |
ea773cfa56 | ||
![]() |
a306561b55 |
25
.github/workflows/pull_request.yml
vendored
Normal file
25
.github/workflows/pull_request.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: PR to main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
MESSAGE: merge branch `${{ github.head_ref || github.ref_name }}` to `main`
|
||||
|
||||
jobs:
|
||||
pull-request:
|
||||
name: Open pull request
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Open pull request
|
||||
uses: repo-sync/pull-request@v2
|
||||
with:
|
||||
destination_branch: 'main'
|
||||
pr_title: 'chore: ${{ env.MESSAGE }}'
|
||||
pr_body: 'This pull request will ${{ env.MESSAGE }}.'
|
||||
pr_draft: true
|
18
.github/workflows/release.yml
vendored
18
.github/workflows/release.yml
vendored
@@ -1,4 +1,5 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
@@ -9,6 +10,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
@@ -17,24 +19,24 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Make sure the release step uses its own credentials:
|
||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
- name: Setup JDK
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
distribution: 'zulu'
|
||||
cache: gradle
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./gradlew build
|
||||
node-version: "latest"
|
||||
cache: 'npm'
|
||||
- name: Setup semantic-release
|
||||
run: npm install -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
|
||||
run: npm install semantic-release@19.0.5 @saithodev/semantic-release-backmerge @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin@1.7.4 -D
|
||||
- name: Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
|
||||
run: npx semantic-release
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -115,3 +115,6 @@ gradle-app.setting
|
||||
|
||||
# Avoid ignoring test resources
|
||||
!src/test/resources/*
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
20
.releaserc
20
.releaserc
@@ -7,11 +7,7 @@
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
["@semantic-release/commit-analyzer", {
|
||||
"releaseRules": [
|
||||
{"type": "build", "release": "patch"}
|
||||
]
|
||||
}],
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
"gradle-semantic-release-plugin",
|
||||
@@ -24,6 +20,18 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"@semantic-release/github"
|
||||
[
|
||||
"@saithodev/semantic-release-backmerge",
|
||||
{
|
||||
branches: [{from: "main", to: "dev"}],
|
||||
clearWorkspace: true
|
||||
}
|
||||
],
|
||||
[
|
||||
"@semantic-release/github",
|
||||
{
|
||||
successComment: false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
91
CHANGELOG.md
91
CHANGELOG.md
@@ -1,3 +1,94 @@
|
||||
## [6.4.1](https://github.com/revanced/revanced-patcher/compare/v6.4.0...v6.4.1) (2023-01-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update dependency `app.revanced:multidexlib2` ([#150](https://github.com/revanced/revanced-patcher/issues/150)) ([dd7dd38](https://github.com/revanced/revanced-patcher/commit/dd7dd383577dcfc95e97f77b446a89b41b589dc0))
|
||||
|
||||
## [6.4.1](https://github.com/revanced/revanced-patcher/compare/v6.4.0...v6.4.1) (2023-01-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update dependency `app.revanced:multidexlib2` ([#150](https://github.com/revanced/revanced-patcher/issues/150)) ([dd7dd38](https://github.com/revanced/revanced-patcher/commit/dd7dd383577dcfc95e97f77b446a89b41b589dc0))
|
||||
|
||||
## [6.4.1-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.4.0...v6.4.1-dev.1) (2023-01-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update dependency `app.revanced:multidexlib2` ([#150](https://github.com/revanced/revanced-patcher/issues/150)) ([dd7dd38](https://github.com/revanced/revanced-patcher/commit/dd7dd383577dcfc95e97f77b446a89b41b589dc0))
|
||||
|
||||
# [6.4.0](https://github.com/revanced/revanced-patcher/compare/v6.3.2...v6.4.0) (2023-01-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add missing setter to `MutableMethod` ([8f3ecc3](https://github.com/revanced/revanced-patcher/commit/8f3ecc318c39f0270aff53efdee7a1c8d82af421))
|
||||
* do not fix methods or methods in class merger ([4102f43](https://github.com/revanced/revanced-patcher/commit/4102f43b8a9473fd0ee96c5d4fb8f6e9b4e30e70))
|
||||
* fix method and field access when merging classes ([5c09ef7](https://github.com/revanced/revanced-patcher/commit/5c09ef7837f9b731e137b66c19da77f63c007595))
|
||||
* make `aaptPath` nullable ([#146](https://github.com/revanced/revanced-patcher/issues/146)) ([9f0a09a](https://github.com/revanced/revanced-patcher/commit/9f0a09a7569fd5dd78afa27cb66a73d1662edc69))
|
||||
|
||||
# [6.4.0-dev.2](https://github.com/revanced/revanced-patcher/compare/v6.4.0-dev.1...v6.4.0-dev.2) (2023-01-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add missing setter to `MutableMethod` ([8f3ecc3](https://github.com/revanced/revanced-patcher/commit/8f3ecc318c39f0270aff53efdee7a1c8d82af421))
|
||||
* do not fix methods or methods in class merger ([4102f43](https://github.com/revanced/revanced-patcher/commit/4102f43b8a9473fd0ee96c5d4fb8f6e9b4e30e70))
|
||||
* fix method and field access when merging classes ([5c09ef7](https://github.com/revanced/revanced-patcher/commit/5c09ef7837f9b731e137b66c19da77f63c007595))
|
||||
|
||||
# [6.4.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.3.2...v6.4.0-dev.1) (2022-12-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* make `aaptPath` nullable ([#146](https://github.com/revanced/revanced-patcher/issues/146)) ([9f0a09a](https://github.com/revanced/revanced-patcher/commit/9f0a09a7569fd5dd78afa27cb66a73d1662edc69))
|
||||
|
||||
## [6.3.2](https://github.com/revanced/revanced-patcher/compare/v6.3.1...v6.3.2) (2022-12-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* check if fingerprint string is substring of any string references ([c5de9e2](https://github.com/revanced/revanced-patcher/commit/c5de9e29889dffd18b31e62a892881cc48e8b607))
|
||||
* print full exception when patch fails ([7cf79e6](https://github.com/revanced/revanced-patcher/commit/7cf79e68e0e9dfd9faddee33139b127b71882d3e))
|
||||
|
||||
## [6.3.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.3.1...v6.3.2-dev.1) (2022-12-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* check if fingerprint string is substring of any string references ([c5de9e2](https://github.com/revanced/revanced-patcher/commit/c5de9e29889dffd18b31e62a892881cc48e8b607))
|
||||
* print full exception when patch fails ([7cf79e6](https://github.com/revanced/revanced-patcher/commit/7cf79e68e0e9dfd9faddee33139b127b71882d3e))
|
||||
|
||||
## [6.3.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.3.1...v6.3.2-dev.1) (2022-12-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* print full exception when patch fails ([27a8401](https://github.com/revanced/revanced-patcher/commit/27a8401d81e078e0303f7ddcb0ac6f342f8e4def))
|
||||
|
||||
## [6.3.1](https://github.com/revanced/revanced-patcher/compare/v6.3.0...v6.3.1) (2022-12-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* publicize types when merging files if necessary ([#137](https://github.com/revanced/revanced-patcher/issues/137)) ([9ec720e](https://github.com/revanced/revanced-patcher/commit/9ec720e983785d8b1dde330cc0e0e0f914c1803c))
|
||||
|
||||
## [6.3.1-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.3.0...v6.3.1-dev.1) (2022-12-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* publicize types when merging files if necessary ([#137](https://github.com/revanced/revanced-patcher/issues/137)) ([9ec720e](https://github.com/revanced/revanced-patcher/commit/9ec720e983785d8b1dde330cc0e0e0f914c1803c))
|
||||
|
||||
# [6.3.0](https://github.com/revanced/revanced-patcher/compare/v6.2.0...v6.3.0) (2022-12-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sort patches in lexicographical order ([a306561](https://github.com/revanced/revanced-patcher/commit/a306561b55ac848792046378f582a036f7ffab03)), closes [#125](https://github.com/revanced/revanced-patcher/issues/125)
|
||||
|
||||
# [6.2.0](https://github.com/revanced/revanced-patcher/compare/v6.1.1...v6.2.0) (2022-12-02)
|
||||
|
||||
|
||||
|
@@ -23,8 +23,8 @@ repositories {
|
||||
dependencies {
|
||||
implementation("xpp3:xpp3:1.1.4c")
|
||||
implementation("org.smali:smali:2.5.2")
|
||||
implementation("app.revanced:multidexlib2:2.5.2.r2")
|
||||
implementation("org.apktool:apktool-lib:2.8.1-SNAPSHOT")
|
||||
implementation("app.revanced:multidexlib2:2.5.3-8649b642")
|
||||
implementation("org.apktool:apktool-lib:2.9.0-SNAPSHOT")
|
||||
|
||||
implementation(kotlin("reflect"))
|
||||
testImplementation(kotlin("test"))
|
||||
|
@@ -1,2 +1,2 @@
|
||||
kotlin.code.style = official
|
||||
version = 6.2.0
|
||||
version = 6.4.1
|
||||
|
6107
package-lock.json
generated
Normal file
6107
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
8
package.json
Normal file
8
package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@semantic-release/changelog": "^6.0.2",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.7.4",
|
||||
"semantic-release": "^19.0.5"
|
||||
}
|
||||
}
|
@@ -7,11 +7,8 @@ import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||
import app.revanced.patcher.extensions.nullOutputStream
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||
import app.revanced.patcher.patch.*
|
||||
import app.revanced.patcher.util.ClassMerger.merge
|
||||
import app.revanced.patcher.util.VersionReader
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import brut.androlib.Androlib
|
||||
import brut.androlib.meta.UsesFramework
|
||||
import brut.androlib.options.BuildOptions
|
||||
@@ -26,9 +23,7 @@ import lanchon.multidexlib2.BasicDexFileNamer
|
||||
import lanchon.multidexlib2.DexIO
|
||||
import lanchon.multidexlib2.MultiDexIO
|
||||
import org.jf.dexlib2.Opcodes
|
||||
import org.jf.dexlib2.iface.ClassDef
|
||||
import org.jf.dexlib2.iface.DexFile
|
||||
import org.jf.dexlib2.util.MethodUtil
|
||||
import org.jf.dexlib2.writer.io.MemoryDataStore
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
@@ -87,66 +82,16 @@ class Patcher(private val options: PatcherOptions) {
|
||||
if (result == null) {
|
||||
logger.trace("Merging type $type")
|
||||
classes.add(classDef)
|
||||
} else {
|
||||
val (existingClass, existingClassIndex) = result
|
||||
continue
|
||||
}
|
||||
|
||||
logger.trace("Type $type exists. Adding missing methods and fields.")
|
||||
val (existingClass, existingClassIndex) = result
|
||||
|
||||
/**
|
||||
* Add missing fields and methods from [from].
|
||||
*
|
||||
* @param from The class to add methods and fields from.
|
||||
*/
|
||||
fun ClassDef.addMissingFrom(from: ClassDef) {
|
||||
var changed = false
|
||||
fun <T> ClassDef.transformClass(transform: (MutableClass) -> T): T {
|
||||
fun toMutableClass() =
|
||||
if (this@transformClass is MutableClass) this else this.toMutable()
|
||||
return transform(toMutableClass())
|
||||
}
|
||||
logger.trace("Type $type exists. Adding missing methods and fields.")
|
||||
|
||||
fun ClassDef.addMissingMethods(): ClassDef {
|
||||
fun getMissingMethods() = from.methods.filterNot {
|
||||
this@addMissingMethods.methods.any { original ->
|
||||
MethodUtil.methodSignaturesMatch(original, it)
|
||||
}
|
||||
}
|
||||
|
||||
return getMissingMethods()
|
||||
.apply {
|
||||
if (isEmpty()) return@addMissingMethods this@addMissingMethods else changed =
|
||||
true
|
||||
}
|
||||
.map { it.toMutable() }
|
||||
.let { missingMethods ->
|
||||
this@addMissingMethods.transformClass { classDef ->
|
||||
classDef.apply { methods.addAll(missingMethods) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ClassDef.addMissingFields(): ClassDef {
|
||||
fun getMissingFields() = from.fields.filterNot {
|
||||
this@addMissingFields.fields.any { original -> original.name == it.name }
|
||||
}
|
||||
|
||||
return getMissingFields()
|
||||
.apply {
|
||||
if (isEmpty()) return@addMissingFields this@addMissingFields else changed = true
|
||||
}
|
||||
.map { it.toMutable() }
|
||||
.let { missingFields ->
|
||||
this@addMissingFields.transformClass { classDef ->
|
||||
classDef.apply { fields.addAll(missingFields) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classes[existingClassIndex] = addMissingMethods().addMissingFields()
|
||||
.apply { if (!changed) return }
|
||||
}
|
||||
|
||||
existingClass.addMissingFrom(classDef)
|
||||
existingClass.merge(classDef, context, logger).let { mergedClass ->
|
||||
if (mergedClass !== existingClass) // referential equality check
|
||||
classes[existingClassIndex] = mergedClass
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,9 +308,10 @@ class Patcher(private val options: PatcherOptions) {
|
||||
val result = executePatch(dependency, executedPatches)
|
||||
if (result.isSuccess()) return@forEach
|
||||
|
||||
val error = result.error()!!
|
||||
val errorMessage = error.cause ?: error.message
|
||||
return PatchResultError("'$patchName' depends on '${dependency.patchName}' but the following error was raised: $errorMessage")
|
||||
return PatchResultError(
|
||||
"'$patchName' depends on '${dependency.patchName}' but the following error was raised: " +
|
||||
result.error()!!.let { it.cause?.stackTraceToString() ?: it.message }
|
||||
)
|
||||
}
|
||||
|
||||
val isResourcePatch = ResourcePatch::class.java.isAssignableFrom(patchClass)
|
||||
|
@@ -15,7 +15,7 @@ import java.io.File
|
||||
data class PatcherOptions(
|
||||
internal val inputFile: File,
|
||||
internal val resourceCacheDirectory: String,
|
||||
internal val aaptPath: String = "",
|
||||
internal val aaptPath: String? = null,
|
||||
internal val frameworkFolderLocation: String? = null,
|
||||
internal val logger: Logger = NopLogger
|
||||
)
|
||||
|
@@ -109,7 +109,7 @@ abstract class MethodFingerprint(
|
||||
if (instruction.opcode.ordinal != Opcode.CONST_STRING.ordinal) return@forEachIndexed
|
||||
|
||||
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||
val index = stringsList.indexOfFirst { it == string }
|
||||
val index = stringsList.indexOfFirst(string::contains)
|
||||
if (index == -1) return@forEachIndexed
|
||||
|
||||
add(
|
||||
|
@@ -14,7 +14,7 @@ import kotlin.reflect.KClass
|
||||
annotation class Patch(val include: Boolean = true)
|
||||
|
||||
/**
|
||||
* Annotation for dependencies of [Patch]es .
|
||||
* Annotation for dependencies of [Patch]es.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
|
214
src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt
Normal file
214
src/main/kotlin/app/revanced/patcher/util/ClassMerger.kt
Normal file
@@ -0,0 +1,214 @@
|
||||
package app.revanced.patcher.util
|
||||
|
||||
import app.revanced.patcher.PatcherContext
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.logging.Logger
|
||||
import app.revanced.patcher.util.ClassMerger.Utils.asMutableClass
|
||||
import app.revanced.patcher.util.ClassMerger.Utils.filterAny
|
||||
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.TypeUtil.traverseClassHierarchy
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField
|
||||
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 org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.iface.ClassDef
|
||||
import org.jf.dexlib2.util.MethodUtil
|
||||
import kotlin.reflect.KFunction2
|
||||
|
||||
/**
|
||||
* Experimental class to merge a [ClassDef] with another.
|
||||
* Note: This will not consider method implementations or if the class is missing a superclass or interfaces.
|
||||
*/
|
||||
internal object ClassMerger {
|
||||
/**
|
||||
* Merge a class with [otherClass].
|
||||
*
|
||||
* @param otherClass The class to merge with
|
||||
* @param context The context to traverse the class hierarchy in.
|
||||
* @param logger A logger.
|
||||
*/
|
||||
fun ClassDef.merge(otherClass: ClassDef, context: PatcherContext, logger: Logger? = null) = this
|
||||
//.fixFieldAccess(otherClass, logger)
|
||||
//.fixMethodAccess(otherClass, logger)
|
||||
.addMissingFields(otherClass, logger)
|
||||
.addMissingMethods(otherClass, logger)
|
||||
.publicize(otherClass, context, logger)
|
||||
|
||||
/**
|
||||
* Add methods which are missing but existing in [fromClass].
|
||||
*
|
||||
* @param fromClass The class to add missing methods from.
|
||||
* @param logger A logger.
|
||||
*/
|
||||
private fun ClassDef.addMissingMethods(fromClass: ClassDef, logger: Logger? = null): ClassDef {
|
||||
val missingMethods = fromClass.methods.let { fromMethods ->
|
||||
methods.filterNot { method ->
|
||||
fromMethods.any { fromMethod ->
|
||||
MethodUtil.methodSignaturesMatch(fromMethod, method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missingMethods.isEmpty()) return this
|
||||
|
||||
logger?.trace("Found ${missingMethods.size} missing methods")
|
||||
|
||||
return asMutableClass().apply {
|
||||
methods.addAll(missingMethods.map { it.toMutable() })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fields which are missing but existing in [fromClass].
|
||||
*
|
||||
* @param fromClass The class to add missing fields from.
|
||||
* @param logger A logger.
|
||||
*/
|
||||
private fun ClassDef.addMissingFields(fromClass: ClassDef, logger: Logger? = null): ClassDef {
|
||||
val missingFields = fields.filterNotAny(fromClass.fields) { field, fromField ->
|
||||
fromField.name == field.name
|
||||
}
|
||||
|
||||
if (missingFields.isEmpty()) return this
|
||||
|
||||
logger?.trace("Found ${missingFields.size} missing fields")
|
||||
|
||||
return asMutableClass().apply {
|
||||
fields.addAll(missingFields.map { it.toMutable() })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a class and its super class public recursively.
|
||||
* @param reference The class to check the [AccessFlags] of.
|
||||
* @param context The context to traverse the class hierarchy in.
|
||||
* @param logger A logger.
|
||||
*/
|
||||
private fun ClassDef.publicize(reference: ClassDef, context: PatcherContext, logger: Logger? = null) =
|
||||
if (reference.accessFlags.isPublic() && !accessFlags.isPublic())
|
||||
this.asMutableClass().apply {
|
||||
context.bytecodeContext.traverseClassHierarchy(this) {
|
||||
if (accessFlags.isPublic()) return@traverseClassHierarchy
|
||||
|
||||
logger?.trace("Publicizing ${this.type}")
|
||||
|
||||
accessFlags = accessFlags.toPublic()
|
||||
}
|
||||
}
|
||||
else this
|
||||
|
||||
/**
|
||||
* Publicize fields if they are public in [reference].
|
||||
*
|
||||
* @param reference The class to check the [AccessFlags] of the fields in.
|
||||
* @param logger A logger.
|
||||
*/
|
||||
private fun ClassDef.fixFieldAccess(reference: ClassDef, logger: Logger? = null): ClassDef {
|
||||
val brokenFields = fields.filterAny(reference.fields) { field, referenceField ->
|
||||
if (field.name != referenceField.name) return@filterAny false
|
||||
|
||||
referenceField.accessFlags.isPublic() && !field.accessFlags.isPublic()
|
||||
}
|
||||
|
||||
if (brokenFields.isEmpty()) return this
|
||||
|
||||
logger?.trace("Found ${brokenFields.size} broken fields")
|
||||
|
||||
/**
|
||||
* Make a field public.
|
||||
*/
|
||||
fun MutableField.publicize() {
|
||||
accessFlags = accessFlags.toPublic()
|
||||
}
|
||||
|
||||
return asMutableClass().apply {
|
||||
fields.filter { brokenFields.contains(it) }.forEach(MutableField::publicize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publicize methods if they are public in [reference].
|
||||
*
|
||||
* @param reference The class to check the [AccessFlags] of the methods in.
|
||||
* @param logger A logger.
|
||||
*/
|
||||
private fun ClassDef.fixMethodAccess(reference: ClassDef, logger: Logger? = null): ClassDef {
|
||||
val brokenMethods = methods.filterAny(reference.methods) { method, referenceMethod ->
|
||||
if (!MethodUtil.methodSignaturesMatch(method, referenceMethod)) return@filterAny false
|
||||
|
||||
referenceMethod.accessFlags.isPublic() && !method.accessFlags.isPublic()
|
||||
}
|
||||
|
||||
if (brokenMethods.isEmpty()) return this
|
||||
|
||||
logger?.trace("Found ${brokenMethods.size} methods")
|
||||
|
||||
/**
|
||||
* Make a method public.
|
||||
*/
|
||||
fun MutableMethod.publicize() {
|
||||
accessFlags = accessFlags.toPublic()
|
||||
}
|
||||
|
||||
return asMutableClass().apply {
|
||||
methods.filter { brokenMethods.contains(it) }.forEach(MutableMethod::publicize)
|
||||
}
|
||||
}
|
||||
|
||||
private object Utils {
|
||||
fun ClassDef.asMutableClass() = if (this is MutableClass) this else this.toMutable()
|
||||
|
||||
/**
|
||||
* Check if the [AccessFlags.PUBLIC] flag is set.
|
||||
*
|
||||
* @return True, if the flag is set.
|
||||
*/
|
||||
fun Int.isPublic() = AccessFlags.PUBLIC.isSet(this)
|
||||
|
||||
/**
|
||||
* Make [AccessFlags] public.
|
||||
*
|
||||
* @return The new [AccessFlags].
|
||||
*/
|
||||
fun Int.toPublic() = this.or(AccessFlags.PUBLIC).and(AccessFlags.PRIVATE.value.inv())
|
||||
|
||||
/**
|
||||
* Filter [this] on [needles] matching the given [predicate].
|
||||
*
|
||||
* @param this The hay to filter for [needles].
|
||||
* @param needles The needles to filter [this] with.
|
||||
* @param predicate The filter.
|
||||
* @return The [this] filtered on [needles] matching the given [predicate].
|
||||
*/
|
||||
fun <HayType, NeedleType> Iterable<HayType>.filterAny(
|
||||
needles: Iterable<NeedleType>, predicate: (HayType, NeedleType) -> Boolean
|
||||
) = Iterable<HayType>::filter.any(this, needles, predicate)
|
||||
|
||||
/**
|
||||
* Filter [this] on [needles] not matching the given [predicate].
|
||||
*
|
||||
* @param this The hay to filter for [needles].
|
||||
* @param needles The needles to filter [this] with.
|
||||
* @param predicate The filter.
|
||||
* @return The [this] filtered on [needles] not matching the given [predicate].
|
||||
*/
|
||||
fun <HayType, NeedleType> Iterable<HayType>.filterNotAny(
|
||||
needles: Iterable<NeedleType>, predicate: (HayType, NeedleType) -> Boolean
|
||||
) = Iterable<HayType>::filterNot.any(this, needles, predicate)
|
||||
|
||||
fun <HayType, NeedleType> KFunction2<Iterable<HayType>, (HayType) -> Boolean, List<HayType>>.any(
|
||||
haystack: Iterable<HayType>,
|
||||
needles: Iterable<NeedleType>,
|
||||
predicate: (HayType, NeedleType) -> Boolean
|
||||
) = this(haystack) { hay ->
|
||||
needles.any { needle ->
|
||||
predicate(hay, needle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
src/main/kotlin/app/revanced/patcher/util/TypeUtil.kt
Normal file
19
src/main/kotlin/app/revanced/patcher/util/TypeUtil.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
package app.revanced.patcher.util
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
|
||||
object TypeUtil {
|
||||
/**
|
||||
* traverse the class hierarchy starting from the given root class
|
||||
*
|
||||
* @param targetClass the class to start traversing the class hierarchy from
|
||||
* @param callback function that is called for every class in the hierarchy
|
||||
*/
|
||||
fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
|
||||
callback(targetClass)
|
||||
this.findClass(targetClass.superclass ?: return)?.mutableClass?.let {
|
||||
traverseClassHierarchy(it, callback)
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,6 +3,7 @@
|
||||
package app.revanced.patcher.util.patch
|
||||
|
||||
import app.revanced.patcher.data.Context
|
||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import org.jf.dexlib2.DexFileFactory
|
||||
import java.io.File
|
||||
@@ -16,12 +17,12 @@ import java.util.jar.JarFile
|
||||
*/
|
||||
sealed class PatchBundle(path: String) : File(path) {
|
||||
internal fun loadPatches(classLoader: ClassLoader, classNames: Iterator<String>) = buildList {
|
||||
for (className in classNames) {
|
||||
classNames.forEach { className ->
|
||||
val clazz = classLoader.loadClass(className)
|
||||
if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) continue
|
||||
if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) return@forEach
|
||||
@Suppress("UNCHECKED_CAST") this.add(clazz as Class<out Patch<Context>>)
|
||||
}
|
||||
}
|
||||
}.sortedBy { it.patchName }
|
||||
|
||||
/**
|
||||
* A patch bundle of type [Jar].
|
||||
|
@@ -20,6 +20,22 @@ class MutableMethod(method: Method) : Method, BaseMethodReference() {
|
||||
private val _parameterTypes by lazy { method.parameterTypes.toMutableList() }
|
||||
private val _hiddenApiRestrictions by lazy { method.hiddenApiRestrictions }
|
||||
|
||||
fun setDefiningClass(definingClass: String) {
|
||||
this.definingClass = definingClass
|
||||
}
|
||||
|
||||
fun setName(name: String) {
|
||||
this.name = name
|
||||
}
|
||||
|
||||
fun setAccessFlags(accessFlags: Int) {
|
||||
this.accessFlags = accessFlags
|
||||
}
|
||||
|
||||
fun setReturnType(returnType: String) {
|
||||
this.returnType = returnType
|
||||
}
|
||||
|
||||
override fun getDefiningClass(): String {
|
||||
return definingClass
|
||||
}
|
||||
|
@@ -1,19 +0,0 @@
|
||||
package app.revanced.patcher.util
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class VersionReaderTest {
|
||||
@Test
|
||||
fun read() {
|
||||
val version = VersionReader.read()
|
||||
assertNotNull(version)
|
||||
assertTrue(version.isNotEmpty())
|
||||
val parts = version.split(".")
|
||||
assertEquals(3, parts.size)
|
||||
parts.forEach {
|
||||
assertTrue(it.toInt() >= 0)
|
||||
}
|
||||
println(version)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user