Compare commits

...

103 Commits

Author SHA1 Message Date
semantic-release-bot
c7f156e4c9 chore(release): 11.0.3-dev.1 [skip ci]
## [11.0.3-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.2...v11.0.3-dev.1) (2023-06-28)

### Bug Fixes

* NPE on method lookup ([#195](https://github.com/revanced/revanced-patcher/issues/195)) ([fcef434](fcef4342e8))
2023-06-28 19:57:40 +00:00
LisoUseInAIKyrios
fcef4342e8 fix: NPE on method lookup (#195) 2023-06-28 21:56:03 +02:00
semantic-release-bot
72783a5e74 chore(release): 11.0.2 [skip ci]
## [11.0.2](https://github.com/revanced/revanced-patcher/compare/v11.0.1...v11.0.2) (2023-06-27)

### Bug Fixes

* catch exceptions from closing patches ([d5d6f85](d5d6f85084))
* do not load annotations as patches ([519359a](519359a9eb))
* only close succeeded patches ([b8151eb](b8151ebccb))
* use `versionCode` if `versionName` is unavailable ([6e1b647](6e1b6479b6))

### Performance Improvements

* resolve fingerprints using method maps ([#185](https://github.com/revanced/revanced-patcher/issues/185)) ([d718134](d718134ab2))
2023-06-27 02:32:47 +00:00
oSumAtrIX
a379b69eeb chore: merge branch dev to main (#191) 2023-06-27 04:31:09 +02:00
semantic-release-bot
0a8ccba33e chore(release): 11.0.2-dev.4 [skip ci]
## [11.0.2-dev.4](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.3...v11.0.2-dev.4) (2023-06-27)

### Bug Fixes

* do not load annotations as patches ([519359a](519359a9eb))
2023-06-27 02:11:33 +00:00
oSumAtrIX
519359a9eb fix: do not load annotations as patches 2023-06-27 04:09:03 +02:00
semantic-release-bot
b615ed6aab chore(release): 11.0.2-dev.3 [skip ci]
## [11.0.2-dev.3](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.2...v11.0.2-dev.3) (2023-06-27)

### Performance Improvements

* resolve fingerprints using method maps ([#185](https://github.com/revanced/revanced-patcher/issues/185)) ([d718134](d718134ab2))
2023-06-27 02:08:29 +00:00
LisoUseInAIKyrios
d718134ab2 perf: resolve fingerprints using method maps (#185)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-06-27 04:06:51 +02:00
semantic-release-bot
5e681ed381 chore(release): 11.0.2-dev.2 [skip ci]
## [11.0.2-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.1...v11.0.2-dev.2) (2023-06-18)

### Bug Fixes

* use `versionCode` if `versionName` is unavailable ([6e1b647](6e1b6479b6))
2023-06-18 14:41:34 +00:00
oSumAtrIX
6e1b6479b6 fix: use versionCode if versionName is unavailable 2023-06-18 16:39:49 +02:00
semantic-release-bot
f3c9e28a62 chore(release): 11.0.2-dev.1 [skip ci]
## [11.0.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.1...v11.0.2-dev.1) (2023-06-14)

### Bug Fixes

* catch exceptions from closing patches ([d5d6f85](d5d6f85084))
* only close succeeded patches ([b8151eb](b8151ebccb))
2023-06-14 00:16:26 +00:00
oSumAtrIX
d5d6f85084 fix: catch exceptions from closing patches 2023-06-14 02:14:37 +02:00
oSumAtrIX
b8151ebccb fix: only close succeeded patches 2023-06-14 01:43:19 +02:00
semantic-release-bot
5650e34432 chore(release): 11.0.1 [skip ci]
## [11.0.1](https://github.com/revanced/revanced-patcher/compare/v11.0.0...v11.0.1) (2023-06-12)

### Bug Fixes

* revert using `OutputStream.nullOutputStream` ([f02a426](f02a42610b))
2023-06-12 03:36:10 +00:00
oSumAtrIX
c893d16d52 chore: merge branch dev to main (#190) 2023-06-12 05:34:23 +02:00
semantic-release-bot
34f08bf206 chore(release): 11.0.1-dev.1 [skip ci]
## [11.0.1-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.0...v11.0.1-dev.1) (2023-06-12)

### Bug Fixes

* revert using `OutputStream.nullOutputStream` ([f02a426](f02a42610b))
2023-06-12 03:33:53 +00:00
oSumAtrIX
f02a42610b fix: revert using OutputStream.nullOutputStream
Older Android versions don't support this API
2023-06-12 05:32:13 +02:00
oSumAtrIX
c95e6fa92f ci: add cache step 2023-06-12 02:55:09 +02:00
oSumAtrIX
fd738e723b ci: build before running semantic-release 2023-06-12 01:52:52 +02:00
oSumAtrIX
b1d1956323 ci: remove unnecessary steps 2023-06-12 01:47:26 +02:00
semantic-release-bot
725a8012ac chore(release): 11.0.0 [skip ci]
# [11.0.0](https://github.com/revanced/revanced-patcher/compare/v10.0.0...v11.0.0) (2023-06-10)

### Bug Fixes

* add imports to fix failing tests ([43d6868](43d6868d1f))

* refactor!: move extension functions to their corresponding classes ([a12fe7d](a12fe7dd9e))
* refactor!: use proper extension function names ([efdd01a](efdd01a988))
* fix!: implement extension functions consistently ([aacf900](aacf900764))

### BREAKING CHANGES

* This changes the import paths for extension functions.
* This changes the names of extension functions
* This changes the name of functions
2023-06-10 23:11:52 +00:00
oSumAtrIX
bb9a73e53b chore: merge branch dev to main (#187) 2023-06-11 01:10:59 +02:00
semantic-release-bot
ef2de35a74 chore(release): 11.0.0-dev.2 [skip ci]
# [11.0.0-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.0-dev.1...v11.0.0-dev.2) (2023-06-09)

### Bug Fixes

* add imports to fix failing tests ([43d6868](43d6868d1f))
2023-06-09 23:56:16 +00:00
oSumAtrIX
2a453d51a8 refactor: rename helper methods for tests 2023-06-10 01:55:05 +02:00
oSumAtrIX
43d6868d1f fix: add imports to fix failing tests 2023-06-10 01:43:09 +02:00
semantic-release-bot
cea9379b32 chore(release): 11.0.0-dev.1 [skip ci]
# [11.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v10.0.0...v11.0.0-dev.1) (2023-06-07)

* refactor!: move extension functions to their corresponding classes ([a12fe7d](a12fe7dd9e))
* refactor!: use proper extension function names ([efdd01a](efdd01a988))
* fix!: implement extension functions consistently ([aacf900](aacf900764))

### BREAKING CHANGES

* This changes the import paths for extension functions.
* This changes the names of extension functions
* This changes the name of functions
2023-06-07 01:45:31 +00:00
oSumAtrIX
a12fe7dd9e refactor!: move extension functions to their corresponding classes
BREAKING CHANGE: This changes the import paths for extension functions.
2023-06-07 03:43:17 +02:00
oSumAtrIX
efdd01a988 refactor!: use proper extension function names
BREAKING CHANGE: This changes the names of extension functions
2023-06-07 03:43:13 +02:00
oSumAtrIX
eafe1c631f refactor: use existing null output stream 2023-06-07 03:32:19 +02:00
oSumAtrIX
aacf900764 fix!: implement extension functions consistently
BREAKING CHANGE: This changes the name of functions
2023-06-07 03:32:19 +02:00
semantic-release-bot
f82494e9bb chore(release): 10.0.0 [skip ci]
# [10.0.0](https://github.com/revanced/revanced-patcher/compare/v9.0.0...v10.0.0) (2023-06-07)

* fix!: check for two methods parameters orders (#183) ([b6d6a75](b6d6a7591b)), closes [#183](https://github.com/revanced/revanced-patcher/issues/183)

### BREAKING CHANGES

* This requires changes to `MethodFingerprint`
2023-06-07 00:59:13 +00:00
oSumAtrIX
1e0ffa176e chore: merge branch dev to main 2023-06-07 02:58:23 +02:00
semantic-release-bot
b7eb2d2249 chore(release): 10.0.0-dev.1 [skip ci]
# [10.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v9.0.0...v10.0.0-dev.1) (2023-06-07)

* fix!: check for two methods parameters orders (#183) ([b6d6a75](b6d6a7591b)), closes [#183](https://github.com/revanced/revanced-patcher/issues/183)

### BREAKING CHANGES

* This requires changes to `MethodFingerprint`
2023-06-07 00:52:08 +00:00
LisoUseInAIKyrios
b6d6a7591b fix!: check for two methods parameters orders (#183)
BREAKING CHANGE: This requires changes to `MethodFingerprint`
2023-06-07 02:50:38 +02:00
semantic-release-bot
8f1c835299 chore(release): 9.0.0 [skip ci]
# [9.0.0](https://github.com/revanced/revanced-patcher/compare/v8.0.0...v9.0.0) (2023-05-23)

* refactor!: rename parameter ([526a3d7](526a3d7c35))

### BREAKING CHANGES

* This changes named parameters.
2023-05-23 23:58:07 +00:00
oSumAtrIX
a188c16a99 chore: merge branch dev to main (#177) 2023-05-24 01:56:40 +02:00
semantic-release-bot
3e6804f06c chore(release): 9.0.0-dev.1 [skip ci]
# [9.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v8.0.0...v9.0.0-dev.1) (2023-05-23)

* refactor!: rename parameter ([526a3d7](526a3d7c35))

### BREAKING CHANGES

* This changes named parameters.
2023-05-23 23:50:12 +00:00
oSumAtrIX
526a3d7c35 refactor!: rename parameter
BREAKING CHANGE: This changes named parameters.
2023-05-24 01:49:07 +02:00
oSumAtrIX
28fc6a2ddd refactor: apply Kotlin idioms 2023-05-24 01:45:46 +02:00
semantic-release-bot
d4f08d7bff chore(release): 8.0.0 [skip ci]
# [8.0.0](https://github.com/revanced/revanced-patcher/compare/v7.1.1...v8.0.0) (2023-05-13)

* feat!: add `classDef` parameter to `MethodFingerprint` (#175) ([a205220](a2052202b2)), closes [#175](https://github.com/revanced/revanced-patcher/issues/175)

### BREAKING CHANGES

* This changes the signature of the `customFingerprint` function.
2023-05-13 23:58:31 +00:00
oSumAtrIX
ca9fe322eb chore: merge branch dev to main (#174) 2023-05-14 01:57:34 +02:00
Jim Man
239ea0bcaa refactor: simplify loading patches (#172) 2023-05-14 01:55:26 +02:00
semantic-release-bot
7f02b8df48 chore(release): 8.0.0-dev.1 [skip ci]
# [8.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v7.1.1...v8.0.0-dev.1) (2023-05-10)

* feat!: add `classDef` parameter to `MethodFingerprint` (#175) ([a205220](a2052202b2)), closes [#175](https://github.com/revanced/revanced-patcher/issues/175)

### BREAKING CHANGES

* This changes the signature of the `customFingerprint` function.
2023-05-10 23:38:38 +00:00
badawoll
a2052202b2 feat!: add classDef parameter to MethodFingerprint (#175)
BREAKING CHANGE: This changes the signature of the `customFingerprint` function.
2023-05-11 01:37:17 +02:00
oSumAtrIX
223cea7021 build: use Java SDK 17 for building 2023-05-09 08:16:15 +02:00
semantic-release-bot
ac9337f694 chore(release): 7.1.1 [skip ci]
## [7.1.1](https://github.com/revanced/revanced-patcher/compare/v7.1.0...v7.1.1) (2023-05-07)

### Bug Fixes

* remove `count` instead of `count + 1` instructions with `removeInstructions` ([#167](https://github.com/revanced/revanced-patcher/issues/167)) ([98f8eed](98f8eedecd))
2023-05-07 23:06:19 +00:00
oSumAtrIX
549651d04a chore: merge branch dev to main (#171) 2023-05-08 01:05:17 +02:00
semantic-release-bot
966bbd902e chore(release): 7.1.1-dev.1 [skip ci]
## [7.1.1-dev.1](https://github.com/revanced/revanced-patcher/compare/v7.1.0...v7.1.1-dev.1) (2023-05-07)

### Bug Fixes

* remove `count` instead of `count + 1` instructions with `removeInstructions` ([#167](https://github.com/revanced/revanced-patcher/issues/167)) ([98f8eed](98f8eedecd))
2023-05-07 23:04:09 +00:00
oSumAtrIX
81e6f8784e docs: fix heading 2023-05-08 01:03:11 +02:00
oSumAtrIX
9c53877888 build: downgrade to JDK version 11
This is to properly support Android
2023-05-08 01:03:11 +02:00
rstular
98f8eedecd fix: remove count instead of count + 1 instructions with removeInstructions (#167)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>

BREAKING-CHANGE: This alters the behaviour of the function. Instead of removing `count + 1` instructions, this now removes `count` instructions.
2023-05-08 01:03:09 +02:00
semantic-release-bot
4ed429d25c chore(release): 7.1.0 [skip ci]
# [7.1.0](https://github.com/revanced/revanced-patcher/compare/v7.0.0...v7.1.0) (2023-05-05)

### Features

* add appreciation message for new contributors ([d674362](d67436271d))
* add overload to get instruction as type ([49c173d](49c173dc14))
2023-05-05 23:06:29 +00:00
oSumAtrIX
119d05f469 chore: merge branch dev to main (#161) 2023-05-06 01:05:01 +02:00
semantic-release-bot
2432fde6bf chore(release): 7.1.0-dev.2 [skip ci]
# [7.1.0-dev.2](https://github.com/revanced/revanced-patcher/compare/v7.1.0-dev.1...v7.1.0-dev.2) (2023-05-05)

### Features

* add overload to get instruction as type ([49c173d](49c173dc14))
2023-05-05 21:38:29 +00:00
oSumAtrIX
49c173dc14 feat: add overload to get instruction as type 2023-05-05 23:36:30 +02:00
oSumAtrIX
d83e9372bb chore: update gradle and dependencies 2023-04-30 05:27:22 +02:00
semantic-release-bot
7e8cd3bede chore(release): 7.1.0-dev.1 [skip ci]
# [7.1.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v7.0.0...v7.1.0-dev.1) (2023-04-30)

### Features

* add appreciation message for new contributors ([d674362](d67436271d))
2023-04-30 01:16:45 +00:00
oSumAtrIX
d67436271d feat: add appreciation message for new contributors 2023-04-30 03:15:12 +02:00
oSumAtrIX
aa07f35f06 chore: bump dependencies 2023-04-03 03:04:05 +02:00
oSumAtrIX
77e0536838 build: update Kotlin to 1.8.10 2023-03-31 17:19:40 +02:00
oSumAtrIX
a49e78234b chore: remove project files
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-03-02 19:23:41 +01:00
semantic-release-bot
a3ae825e48 chore(release): 7.0.0 [skip ci]
# [7.0.0](https://github.com/revanced/revanced-patcher/compare/v6.4.3...v7.0.0) (2023-02-26)

* feat!: merge integrations only when necessary ([6e24a85](6e24a85eab))

### BREAKING CHANGES

* `Patcher.addFiles` is now renamed to `Patcher.addIntegrations`

Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-26 23:42:48 +00:00
oSumAtrIX
146c8504ed chore: merge branch dev to main (#159) 2023-02-27 00:41:27 +01:00
semantic-release-bot
2eb125ad69 chore(release): 7.0.0-dev.1 [skip ci]
# [7.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.4.3...v7.0.0-dev.1) (2023-02-26)

* feat!: merge integrations only when necessary ([6e24a85](6e24a85eab))

### BREAKING CHANGES

* `Patcher.addFiles` is now renamed to `Patcher.addIntegrations`

Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-26 23:24:38 +00:00
oSumAtrIX
6e24a85eab feat!: merge integrations only when necessary
BREAKING CHANGE: `Patcher.addFiles` is now renamed to `Patcher.addIntegrations`

Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-27 00:23:27 +01:00
oSumAtrIX
e4c3e9ffc5 refactor: remove unnecessary annotations
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-26 23:18:35 +01:00
Javier Flores
4c1778a62f chore: update gradle wrapper to 8.0.1 (#160) 2023-02-26 03:46:33 +01:00
oSumAtrIX
d99261cdbb ci: fix backmerge direction
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-22 16:39:50 +01:00
oSumAtrIX
ac1c0f2773 ci: add backmerge target branch
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-22 06:45:16 +01:00
oSumAtrIX
eddd4ec7ac ci: use new semantic-release-backmerge option
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-18 21:55:40 +01:00
Palm
07a2829c65 ci: update actions (#158) 2023-02-13 22:29:20 +01:00
semantic-release-bot
3d77e299d9 chore(release): 6.4.3 [skip ci]
## [6.4.3](https://github.com/revanced/revanced-patcher/compare/v6.4.2...v6.4.3) (2023-02-10)

### Bug Fixes

* check `CONST_STRING_JUMP` instructions for matching string ([058d292](058d292ad5))
2023-02-10 04:28:15 +00:00
oSumAtrIX
f1336f89e4 chore: merge branch dev to main (#156) 2023-02-10 05:26:43 +01:00
semantic-release-bot
0502f84c20 chore(release): 6.4.3-dev.1 [skip ci]
## [6.4.3-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.4.2...v6.4.3-dev.1) (2023-02-10)

### Bug Fixes

* check `CONST_STRING_JUMP` instructions for matching string ([058d292](058d292ad5))
2023-02-10 04:20:46 +00:00
oSumAtrIX
058d292ad5 fix: check CONST_STRING_JUMP instructions for matching string
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-10 05:18:38 +01:00
oSumAtrIX
1029d56a52 refactor: remove dead code
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-10 05:17:36 +01:00
oSumAtrIX
709b5a0fec chore: fix typo
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-02-10 05:16:00 +01:00
semantic-release-bot
e1accc5041 chore(release): 6.4.2 [skip ci]
## [6.4.2](https://github.com/revanced/revanced-patcher/compare/v6.4.1...v6.4.2) (2023-01-17)

### Bug Fixes

* resolve failing builds ([a263fdf](a263fdfd41))
2023-01-17 23:29:34 +00:00
oSumAtrIX
6dbbf2e03e chore: merge branch dev to main (#153) 2023-01-18 00:14:38 +01:00
semantic-release-bot
16557eeab0 chore(release): 6.4.2-dev.1 [skip ci]
## [6.4.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.4.1...v6.4.2-dev.1) (2023-01-17)

### Bug Fixes

* resolve failing builds ([a263fdf](a263fdfd41))
2023-01-17 23:09:26 +00:00
oSumAtrIX
6bca3e2bb5 build: update dependencies 2023-01-17 23:57:19 +01:00
oSumAtrIX
a263fdfd41 fix: resolve failing builds
This updates dependencies, because the previous ones were missing or outdated.
2023-01-17 01:32:11 +01:00
semantic-release-bot
e4b4bacae8 chore(release): 6.4.1 [skip ci]
## [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](dd7dd38357))
2023-01-15 05:13:48 +00:00
semantic-release-bot
cbc97af155 chore(release): 6.4.1 [skip ci]
## [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](dd7dd38357))
2023-01-15 04:32:33 +00:00
oSumAtrIX
d5533788e2 chore: merge branch dev to main (#152) 2023-01-15 05:31:12 +01:00
oSumAtrIX
5a4ea5cd7d ci: add missing PAT in step Release 2023-01-15 05:29:50 +01:00
oSumAtrIX
70f3c8b38c chore: merge branch dev to main (#151) 2023-01-15 05:24:25 +01:00
semantic-release-bot
6b410a0eea chore(release): 6.4.1-dev.1 [skip ci]
## [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](dd7dd38357))
2023-01-15 03:07:43 +00:00
oSumAtrIX
73a013d75b ci: use PAT in step Release 2023-01-15 04:06:45 +01:00
oSumAtrIX
7159f3db4c ci: downgrade semantic release and plugins to v19 2023-01-15 04:06:45 +01:00
oSumAtrIX
7d5ecf095c ci: give jobs names 2023-01-15 04:06:44 +01:00
oSumAtrIX
fa015a424d refactor: fix formatting 2023-01-15 04:06:44 +01:00
oSumAtrIX
dd7dd38357 fix: update dependency app.revanced:multidexlib2 (#150)
This commit addresses https://github.com/revanced/multidexlib2/issues/2.
2023-01-15 04:06:43 +01:00
semantic-release-bot
22356f2d26 chore(release): 6.4.0 [skip ci]
# [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](8f3ecc318c))
* do not fix methods or methods in class merger ([4102f43](4102f43b8a))
* fix method and field access when merging classes ([5c09ef7](5c09ef7837))
* make `aaptPath` nullable ([#146](https://github.com/revanced/revanced-patcher/issues/146)) ([9f0a09a](9f0a09a756))
2023-01-02 08:07:08 +00:00
oSumAtrIX
66701f6076 chore: merge branch dev to main (#147) 2023-01-02 09:05:19 +01:00
semantic-release-bot
6a6ded084e chore(release): 6.4.0-dev.2 [skip ci]
# [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](8f3ecc318c))
* do not fix methods or methods in class merger ([4102f43](4102f43b8a))
* fix method and field access when merging classes ([5c09ef7](5c09ef7837))
2023-01-02 07:58:55 +00:00
oSumAtrIX
5887c69bde refactor: move merging classes code to own class (#149) 2023-01-02 08:58:05 +01:00
oSumAtrIX
4102f43b8a feat: do not fix methods or methods in class merger 2023-01-02 08:50:08 +01:00
oSumAtrIX
5c09ef7837 feat: fix method and field access when merging classes 2023-01-02 07:57:50 +01:00
oSumAtrIX
3e0bf8c863 refactor: move merging classes code to own class 2023-01-02 07:57:48 +01:00
oSumAtrIX
8f3ecc318c feat: add missing setter to MutableMethod 2023-01-02 07:09:58 +01:00
oSumAtrIX
365da96e2b build: do not comment on successful releases 2022-12-31 21:12:03 +01:00
oSumAtrIX
cd68ec4803 ci: do not escape in environment variable MESSAGE 2022-12-29 19:07:31 +01:00
43 changed files with 2331 additions and 854 deletions

2
.github/config.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
firstPRMergeComment: >
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.

View File

@@ -7,10 +7,11 @@ on:
workflow_dispatch:
env:
MESSAGE: merge branch \`${{ github.head_ref || github.ref_name }}\` to \`main\`
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

View File

@@ -1,4 +1,5 @@
name: Release
on:
workflow_dispatch:
push:
@@ -9,6 +10,7 @@ on:
branches:
- main
- dev
jobs:
release:
name: Release
@@ -17,21 +19,27 @@ 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
- name: Cache
uses: actions/cache@v3
with:
java-version: '17'
distribution: 'zulu'
cache: gradle
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "latest"
cache: 'npm'
- name: Setup semantic-release
run: npm install semantic-release @saithodev/semantic-release-backmerge @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
- name: Release
path: |
${{ runner.home }}/.gradle/caches
${{ runner.home }}/.gradle/wrapper
.gradle
build
node_modules
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
- name: Build with Gradle
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
run: ./gradlew clean --no-daemon
- name: Setup semantic-release
run: npm install
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
run: npm exec semantic-release

6
.gitignore vendored
View File

@@ -74,6 +74,7 @@ cmake-build-*/
# IntelliJ
out/
.idea/
# mpeltonen/sbt-idea plugin
.idea_modules/
@@ -117,4 +118,7 @@ gradle-app.setting
!src/test/resources/*
# Dependency directories
node_modules/
node_modules/
# Gradle props, to avoid sharing the gpr key
gradle.properties

9
.idea/.gitignore generated vendored
View File

@@ -1,9 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
/kotlinc.xml

View File

@@ -1,10 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

7
.idea/discord.xml generated
View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
</component>
</project>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxProjectSettings">
<option name="commitMessageIssueKeyValidationOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
<option name="commitMessageValidationEnabledOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
</component>
</project>

View File

@@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="UnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

10
.idea/misc.xml generated
View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="azul-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

12
.idea/vcs.xml generated
View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CommitMessageInspectionProfile">
<profile version="1.0">
<inspection_tool class="CommitFormat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CommitNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -23,10 +23,15 @@
[
"@saithodev/semantic-release-backmerge",
{
branches: [{from: "main", to: "dev"}],
backmergeBranches: [{"from": "main", "to": "dev"}],
clearWorkspace: true
}
],
"@semantic-release/github"
[
"@semantic-release/github",
{
successComment: false
}
]
]
}

View File

@@ -1,3 +1,296 @@
## [11.0.3-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.2...v11.0.3-dev.1) (2023-06-28)
### Bug Fixes
* NPE on method lookup ([#195](https://github.com/revanced/revanced-patcher/issues/195)) ([fcef434](https://github.com/revanced/revanced-patcher/commit/fcef4342e8bde73945e8315aef6337cc8a8d8572))
## [11.0.2](https://github.com/revanced/revanced-patcher/compare/v11.0.1...v11.0.2) (2023-06-27)
### Bug Fixes
* catch exceptions from closing patches ([d5d6f85](https://github.com/revanced/revanced-patcher/commit/d5d6f85084c03ed9c776632823ca12394a716167))
* do not load annotations as patches ([519359a](https://github.com/revanced/revanced-patcher/commit/519359a9eb0e9dfa390c5016e9fe4a7490b8ab18))
* only close succeeded patches ([b8151eb](https://github.com/revanced/revanced-patcher/commit/b8151ebccb5b27dd9e06fa63235cf9baeef1c0ee))
* use `versionCode` if `versionName` is unavailable ([6e1b647](https://github.com/revanced/revanced-patcher/commit/6e1b6479b677657c226693e9cc6b63f4ef2ee060))
### Performance Improvements
* resolve fingerprints using method maps ([#185](https://github.com/revanced/revanced-patcher/issues/185)) ([d718134](https://github.com/revanced/revanced-patcher/commit/d718134ab26423e02708e01eba711737f9260ba0))
## [11.0.2-dev.4](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.3...v11.0.2-dev.4) (2023-06-27)
### Bug Fixes
* do not load annotations as patches ([519359a](https://github.com/revanced/revanced-patcher/commit/519359a9eb0e9dfa390c5016e9fe4a7490b8ab18))
## [11.0.2-dev.3](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.2...v11.0.2-dev.3) (2023-06-27)
### Performance Improvements
* resolve fingerprints using method maps ([#185](https://github.com/revanced/revanced-patcher/issues/185)) ([d718134](https://github.com/revanced/revanced-patcher/commit/d718134ab26423e02708e01eba711737f9260ba0))
## [11.0.2-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.1...v11.0.2-dev.2) (2023-06-18)
### Bug Fixes
* use `versionCode` if `versionName` is unavailable ([6e1b647](https://github.com/revanced/revanced-patcher/commit/6e1b6479b677657c226693e9cc6b63f4ef2ee060))
## [11.0.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.1...v11.0.2-dev.1) (2023-06-14)
### Bug Fixes
* catch exceptions from closing patches ([d5d6f85](https://github.com/revanced/revanced-patcher/commit/d5d6f85084c03ed9c776632823ca12394a716167))
* only close succeeded patches ([b8151eb](https://github.com/revanced/revanced-patcher/commit/b8151ebccb5b27dd9e06fa63235cf9baeef1c0ee))
## [11.0.1](https://github.com/revanced/revanced-patcher/compare/v11.0.0...v11.0.1) (2023-06-12)
### Bug Fixes
* revert using `OutputStream.nullOutputStream` ([f02a426](https://github.com/revanced/revanced-patcher/commit/f02a42610b7698fc8cc6bc5183c9ccba2ed96cda))
## [11.0.1-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.0...v11.0.1-dev.1) (2023-06-12)
### Bug Fixes
* revert using `OutputStream.nullOutputStream` ([f02a426](https://github.com/revanced/revanced-patcher/commit/f02a42610b7698fc8cc6bc5183c9ccba2ed96cda))
# [11.0.0](https://github.com/revanced/revanced-patcher/compare/v10.0.0...v11.0.0) (2023-06-10)
### Bug Fixes
* add imports to fix failing tests ([43d6868](https://github.com/revanced/revanced-patcher/commit/43d6868d1f59922f9812f3e4a2a890f3b331def6))
* refactor!: move extension functions to their corresponding classes ([a12fe7d](https://github.com/revanced/revanced-patcher/commit/a12fe7dd9e976c38a0a82fe35e6650f58f815de4))
* refactor!: use proper extension function names ([efdd01a](https://github.com/revanced/revanced-patcher/commit/efdd01a9886b6f06af213731824621964367b2a3))
* fix!: implement extension functions consistently ([aacf900](https://github.com/revanced/revanced-patcher/commit/aacf9007647b1cc11bc40053625802573efda6ef))
### BREAKING CHANGES
* This changes the import paths for extension functions.
* This changes the names of extension functions
* This changes the name of functions
# [11.0.0-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.0-dev.1...v11.0.0-dev.2) (2023-06-09)
### Bug Fixes
* add imports to fix failing tests ([43d6868](https://github.com/revanced/revanced-patcher/commit/43d6868d1f59922f9812f3e4a2a890f3b331def6))
# [11.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v10.0.0...v11.0.0-dev.1) (2023-06-07)
* refactor!: move extension functions to their corresponding classes ([a12fe7d](https://github.com/revanced/revanced-patcher/commit/a12fe7dd9e976c38a0a82fe35e6650f58f815de4))
* refactor!: use proper extension function names ([efdd01a](https://github.com/revanced/revanced-patcher/commit/efdd01a9886b6f06af213731824621964367b2a3))
* fix!: implement extension functions consistently ([aacf900](https://github.com/revanced/revanced-patcher/commit/aacf9007647b1cc11bc40053625802573efda6ef))
### BREAKING CHANGES
* This changes the import paths for extension functions.
* This changes the names of extension functions
* This changes the name of functions
# [10.0.0](https://github.com/revanced/revanced-patcher/compare/v9.0.0...v10.0.0) (2023-06-07)
* fix!: check for two methods parameters orders (#183) ([b6d6a75](https://github.com/revanced/revanced-patcher/commit/b6d6a7591ba1c0b48ffdef52352709564da8d9be)), closes [#183](https://github.com/revanced/revanced-patcher/issues/183)
### BREAKING CHANGES
* This requires changes to `MethodFingerprint`
# [10.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v9.0.0...v10.0.0-dev.1) (2023-06-07)
* fix!: check for two methods parameters orders (#183) ([b6d6a75](https://github.com/revanced/revanced-patcher/commit/b6d6a7591ba1c0b48ffdef52352709564da8d9be)), closes [#183](https://github.com/revanced/revanced-patcher/issues/183)
### BREAKING CHANGES
* This requires changes to `MethodFingerprint`
# [9.0.0](https://github.com/revanced/revanced-patcher/compare/v8.0.0...v9.0.0) (2023-05-23)
* refactor!: rename parameter ([526a3d7](https://github.com/revanced/revanced-patcher/commit/526a3d7c359e2d95d26756da0f88d5ce975f5d9b))
### BREAKING CHANGES
* This changes named parameters.
# [9.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v8.0.0...v9.0.0-dev.1) (2023-05-23)
* refactor!: rename parameter ([526a3d7](https://github.com/revanced/revanced-patcher/commit/526a3d7c359e2d95d26756da0f88d5ce975f5d9b))
### BREAKING CHANGES
* This changes named parameters.
# [8.0.0](https://github.com/revanced/revanced-patcher/compare/v7.1.1...v8.0.0) (2023-05-13)
* feat!: add `classDef` parameter to `MethodFingerprint` (#175) ([a205220](https://github.com/revanced/revanced-patcher/commit/a2052202b23037150df6aadc47f6e91efcd481cf)), closes [#175](https://github.com/revanced/revanced-patcher/issues/175)
### BREAKING CHANGES
* This changes the signature of the `customFingerprint` function.
# [8.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v7.1.1...v8.0.0-dev.1) (2023-05-10)
* feat!: add `classDef` parameter to `MethodFingerprint` (#175) ([a205220](https://github.com/revanced/revanced-patcher/commit/a2052202b23037150df6aadc47f6e91efcd481cf)), closes [#175](https://github.com/revanced/revanced-patcher/issues/175)
### BREAKING CHANGES
* This changes the signature of the `customFingerprint` function.
## [7.1.1](https://github.com/revanced/revanced-patcher/compare/v7.1.0...v7.1.1) (2023-05-07)
### Bug Fixes
* remove `count` instead of `count + 1` instructions with `removeInstructions` ([#167](https://github.com/revanced/revanced-patcher/issues/167)) ([98f8eed](https://github.com/revanced/revanced-patcher/commit/98f8eedecd72b0afe6a0f099a3641a1cc6be2698))
## [7.1.1-dev.1](https://github.com/revanced/revanced-patcher/compare/v7.1.0...v7.1.1-dev.1) (2023-05-07)
### Bug Fixes
* remove `count` instead of `count + 1` instructions with `removeInstructions` ([#167](https://github.com/revanced/revanced-patcher/issues/167)) ([98f8eed](https://github.com/revanced/revanced-patcher/commit/98f8eedecd72b0afe6a0f099a3641a1cc6be2698))
# [7.1.0](https://github.com/revanced/revanced-patcher/compare/v7.0.0...v7.1.0) (2023-05-05)
### Features
* add appreciation message for new contributors ([d674362](https://github.com/revanced/revanced-patcher/commit/d67436271ddca9ccfe008272c1ca82c6123ae7ee))
* add overload to get instruction as type ([49c173d](https://github.com/revanced/revanced-patcher/commit/49c173dc14137ddd198a611e9882dc71300831ee))
# [7.1.0-dev.2](https://github.com/revanced/revanced-patcher/compare/v7.1.0-dev.1...v7.1.0-dev.2) (2023-05-05)
### Features
* add overload to get instruction as type ([49c173d](https://github.com/revanced/revanced-patcher/commit/49c173dc14137ddd198a611e9882dc71300831ee))
# [7.1.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v7.0.0...v7.1.0-dev.1) (2023-04-30)
### Features
* add appreciation message for new contributors ([d674362](https://github.com/revanced/revanced-patcher/commit/d67436271ddca9ccfe008272c1ca82c6123ae7ee))
# [7.0.0](https://github.com/revanced/revanced-patcher/compare/v6.4.3...v7.0.0) (2023-02-26)
* feat!: merge integrations only when necessary ([6e24a85](https://github.com/revanced/revanced-patcher/commit/6e24a85eabd1e7a1484fad229d5ba55c3ba1f1b4))
### BREAKING CHANGES
* `Patcher.addFiles` is now renamed to `Patcher.addIntegrations`
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
# [7.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.4.3...v7.0.0-dev.1) (2023-02-26)
* feat!: merge integrations only when necessary ([6e24a85](https://github.com/revanced/revanced-patcher/commit/6e24a85eabd1e7a1484fad229d5ba55c3ba1f1b4))
### BREAKING CHANGES
* `Patcher.addFiles` is now renamed to `Patcher.addIntegrations`
Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
## [6.4.3](https://github.com/revanced/revanced-patcher/compare/v6.4.2...v6.4.3) (2023-02-10)
### Bug Fixes
* check `CONST_STRING_JUMP` instructions for matching string ([058d292](https://github.com/revanced/revanced-patcher/commit/058d292ad5e297f4c652ff543c13e77a39f7fb1b))
## [6.4.3-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.4.2...v6.4.3-dev.1) (2023-02-10)
### Bug Fixes
* check `CONST_STRING_JUMP` instructions for matching string ([058d292](https://github.com/revanced/revanced-patcher/commit/058d292ad5e297f4c652ff543c13e77a39f7fb1b))
## [6.4.2](https://github.com/revanced/revanced-patcher/compare/v6.4.1...v6.4.2) (2023-01-17)
### Bug Fixes
* resolve failing builds ([a263fdf](https://github.com/revanced/revanced-patcher/commit/a263fdfd413fc05098e28d4800e36ce7d313085b))
## [6.4.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v6.4.1...v6.4.2-dev.1) (2023-01-17)
### Bug Fixes
* resolve failing builds ([a263fdf](https://github.com/revanced/revanced-patcher/commit/a263fdfd413fc05098e28d4800e36ce7d313085b))
## [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)

View File

@@ -1,2 +1,3 @@
# Patcher
Patcher framework used in the ReVanced project.
# 💉 ReVanced Patcher
ReVanced Patcher used to patch Android applications.

View File

@@ -1,6 +1,5 @@
plugins {
kotlin("jvm") version "1.7.0"
java
kotlin("jvm") version "1.8.10"
`maven-publish`
}
@@ -22,12 +21,12 @@ 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.9.0-SNAPSHOT")
implementation("app.revanced:smali:2.5.3-a3836654")
implementation("app.revanced:multidexlib2:2.5.3-a3836654")
implementation("app.revanced:apktool-lib:2.7.0")
implementation(kotlin("reflect"))
testImplementation(kotlin("test"))
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.20-RC")
testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC")
}
tasks {
@@ -46,6 +45,10 @@ java {
withSourcesJar()
}
kotlin {
jvmToolchain(11)
}
publishing {
repositories {
if (System.getenv("GITHUB_ACTOR") != null)

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official
version = 6.4.0-dev.1
version = 11.0.3-dev.1

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

727
package-lock.json generated
View File

@@ -5,10 +5,11 @@
"packages": {
"": {
"devDependencies": {
"@saithodev/semantic-release-backmerge": "^3.1.0",
"@semantic-release/changelog": "^6.0.2",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.7.4",
"semantic-release": "^19.0.5"
"gradle-semantic-release-plugin": "^1.7.6",
"semantic-release": "^20.1.0"
}
},
"node_modules/@babel/code-frame": {
@@ -312,6 +313,20 @@
"@octokit/openapi-types": "^14.0.0"
}
},
"node_modules/@saithodev/semantic-release-backmerge": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@saithodev/semantic-release-backmerge/-/semantic-release-backmerge-3.1.0.tgz",
"integrity": "sha512-92AN5eI8svpxeUD6cw2JjCrHHZVlWIxQ67SiSSwoI1UP4N5QohCOf9O/W3OUApxKg3C8Y0RpGt7TUpGEwGhXhw==",
"dev": true,
"dependencies": {
"@semantic-release/error": "^3.0.0",
"aggregate-error": "^3.1.0",
"debug": "^4.3.4",
"execa": "^5.1.1",
"lodash": "^4.17.21",
"semantic-release": ">=20.0.0"
}
},
"node_modules/@semantic-release/changelog": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.2.tgz",
@@ -498,12 +513,6 @@
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
"dev": true
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
"node_modules/@types/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
@@ -580,6 +589,12 @@
"integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
"dev": true
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"node_modules/argv-formatter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz",
@@ -735,14 +750,17 @@
}
},
"node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
@@ -887,19 +905,18 @@
"dev": true
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz",
"integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==",
"dev": true,
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
"parse-json": "^5.0.0",
"path-type": "^4.0.0",
"yaml": "^1.10.0"
"path-type": "^4.0.0"
},
"engines": {
"node": ">=10"
"node": ">=14"
}
},
"node_modules/cross-spawn": {
@@ -1077,17 +1094,126 @@
"dev": true
},
"node_modules/env-ci": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.5.0.tgz",
"integrity": "sha512-o0JdWIbOLP+WJKIUt36hz1ImQQFuN92nhsfTkHHap+J8CiI8WgGpH/a9jEGHh4/TU5BUUGjlnKXNoDb57+ne+A==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/env-ci/-/env-ci-8.0.0.tgz",
"integrity": "sha512-W+3BqGZozFua9MPeXpmTm5eYEBtGgL76jGu/pwMVp/L8PdECSCEWaIp7d4Mw7kuUrbUldK0oV0bNd6ZZjLiMiA==",
"dev": true,
"dependencies": {
"execa": "^5.0.0",
"fromentries": "^1.3.2",
"java-properties": "^1.0.0"
"execa": "^6.1.0",
"java-properties": "^1.0.2"
},
"engines": {
"node": ">=10.17"
"node": "^16.10 || >=18"
}
},
"node_modules/env-ci/node_modules/execa": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz",
"integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.3",
"get-stream": "^6.0.1",
"human-signals": "^3.0.1",
"is-stream": "^3.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^5.1.0",
"onetime": "^6.0.0",
"signal-exit": "^3.0.7",
"strip-final-newline": "^3.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/env-ci/node_modules/human-signals": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz",
"integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==",
"dev": true,
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/env-ci/node_modules/is-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/env-ci/node_modules/mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/env-ci/node_modules/npm-run-path": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
"integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
"dev": true,
"dependencies": {
"path-key": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/env-ci/node_modules/onetime": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
"dev": true,
"dependencies": {
"mimic-fn": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/env-ci/node_modules/path-key": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/env-ci/node_modules/strip-final-newline": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/error-ex": {
@@ -1179,15 +1305,28 @@
}
},
"node_modules/figures": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz",
"integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==",
"dev": true,
"dependencies": {
"escape-string-regexp": "^1.0.5"
"escape-string-regexp": "^5.0.0",
"is-unicode-supported": "^1.2.0"
},
"engines": {
"node": ">=8"
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/figures/node_modules/escape-string-regexp": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -1219,15 +1358,15 @@
}
},
"node_modules/find-versions": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz",
"integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz",
"integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==",
"dev": true,
"dependencies": {
"semver-regex": "^3.1.2"
"semver-regex": "^4.0.5"
},
"engines": {
"node": ">=10"
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -1243,26 +1382,6 @@
"readable-stream": "^2.0.0"
}
},
"node_modules/fromentries": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
"integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/fs-extra": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz",
@@ -1402,9 +1521,9 @@
"dev": true
},
"node_modules/gradle-semantic-release-plugin": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/gradle-semantic-release-plugin/-/gradle-semantic-release-plugin-1.7.4.tgz",
"integrity": "sha512-Mm2JnmodHxQlCmn0GtSi5j8z4PS2+2VBY993b9GuNGGM+JaIrr8T3tF9uTLR2nGH/oJba6a9jx1ZqVEJdOXDPw==",
"version": "1.7.6",
"resolved": "https://registry.npmjs.org/gradle-semantic-release-plugin/-/gradle-semantic-release-plugin-1.7.6.tgz",
"integrity": "sha512-FNoZAm9jntxOXLee5uJLlCb9hsFsJ1d4jUnvz08NF6p72OwSmaSBeFN7Wnl6RjW8mPrAuOSwkuinuWWjVeO2aw==",
"dev": true,
"funding": [
{
@@ -1413,14 +1532,14 @@
}
],
"dependencies": {
"promisified-properties": "^2.0.3",
"promisified-properties": "^2.0.27",
"split2": "^4.1.0"
},
"engines": {
"node": ">=16 || ^14.17"
"node": ">=18"
},
"peerDependencies": {
"semantic-release": "^19.0.2"
"semantic-release": "^20.0.0"
}
},
"node_modules/handlebars": {
@@ -1475,12 +1594,15 @@
}
},
"node_modules/hook-std": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz",
"integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz",
"integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==",
"dev": true,
"engines": {
"node": ">=8"
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/hosted-git-info": {
@@ -1750,6 +1872,18 @@
"node": ">=0.10.0"
}
},
"node_modules/is-unicode-supported": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
"integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@@ -1793,6 +1927,18 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@@ -1909,6 +2055,12 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"dev": true
},
"node_modules/lodash.capitalize": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz",
@@ -4817,12 +4969,12 @@
}
},
"node_modules/p-each-series": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
"integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz",
"integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==",
"dev": true,
"engines": {
"node": ">=8"
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -5102,9 +5254,9 @@
"dev": true
},
"node_modules/promisified-properties": {
"version": "2.0.25",
"resolved": "https://registry.npmjs.org/promisified-properties/-/promisified-properties-2.0.25.tgz",
"integrity": "sha512-FtyMDSNf3IwVBjVq4uj5JD4lG+YmOMzt/Huuc+VwPHVqbzim3/U04iMBnVH3lYgOc9s5Jj9sC4G0OVGoo2KbWA==",
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/promisified-properties/-/promisified-properties-2.0.27.tgz",
"integrity": "sha512-fmx256M3b0QcHnqOj+Ok127LoYpmnYRf7g2OyLl7qD7Z0fzNbIZhHHIPKyvegbA29iAPP5BVWv7BJ/y2cMHHjA==",
"dev": true,
"dependencies": {
"parsimmon": "^1.13.0"
@@ -5393,9 +5545,9 @@
"dev": true
},
"node_modules/semantic-release": {
"version": "19.0.5",
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-19.0.5.tgz",
"integrity": "sha512-NMPKdfpXTnPn49FDogMBi36SiBfXkSOJqCkk0E4iWOY1tusvvgBwqUmxTX1kmlT6kIYed9YwNKD1sfPpqa5yaA==",
"version": "20.1.0",
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-20.1.0.tgz",
"integrity": "sha512-+9+n6RIr0Fz0F53cXrjpawxWlUg3O7/qr1jF9lrE+/v6WqwBrSWnavVHTPaf2WLerET2EngoqI0M4pahkKl6XQ==",
"dev": true,
"dependencies": {
"@semantic-release/commit-analyzer": "^9.0.2",
@@ -5403,35 +5555,350 @@
"@semantic-release/github": "^8.0.0",
"@semantic-release/npm": "^9.0.0",
"@semantic-release/release-notes-generator": "^10.0.0",
"aggregate-error": "^3.0.0",
"cosmiconfig": "^7.0.0",
"aggregate-error": "^4.0.1",
"cosmiconfig": "^8.0.0",
"debug": "^4.0.0",
"env-ci": "^5.0.0",
"execa": "^5.0.0",
"figures": "^3.0.0",
"find-versions": "^4.0.0",
"env-ci": "^8.0.0",
"execa": "^6.1.0",
"figures": "^5.0.0",
"find-versions": "^5.1.0",
"get-stream": "^6.0.0",
"git-log-parser": "^1.2.0",
"hook-std": "^2.0.0",
"hosted-git-info": "^4.0.0",
"lodash": "^4.17.21",
"marked": "^4.0.10",
"marked-terminal": "^5.0.0",
"hook-std": "^3.0.0",
"hosted-git-info": "^6.0.0",
"lodash-es": "^4.17.21",
"marked": "^4.1.0",
"marked-terminal": "^5.1.1",
"micromatch": "^4.0.2",
"p-each-series": "^2.1.0",
"p-reduce": "^2.0.0",
"read-pkg-up": "^7.0.0",
"p-each-series": "^3.0.0",
"p-reduce": "^3.0.0",
"read-pkg-up": "^9.1.0",
"resolve-from": "^5.0.0",
"semver": "^7.3.2",
"semver-diff": "^3.1.1",
"semver-diff": "^4.0.0",
"signale": "^1.2.1",
"yargs": "^16.2.0"
"yargs": "^17.5.1"
},
"bin": {
"semantic-release": "bin/semantic-release.js"
},
"engines": {
"node": ">=16 || ^14.17"
"node": ">=18"
}
},
"node_modules/semantic-release/node_modules/aggregate-error": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz",
"integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==",
"dev": true,
"dependencies": {
"clean-stack": "^4.0.0",
"indent-string": "^5.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/clean-stack": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz",
"integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==",
"dev": true,
"dependencies": {
"escape-string-regexp": "5.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/escape-string-regexp": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/execa": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz",
"integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.3",
"get-stream": "^6.0.1",
"human-signals": "^3.0.1",
"is-stream": "^3.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^5.1.0",
"onetime": "^6.0.0",
"signal-exit": "^3.0.7",
"strip-final-newline": "^3.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/semantic-release/node_modules/find-up": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz",
"integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==",
"dev": true,
"dependencies": {
"locate-path": "^7.1.0",
"path-exists": "^5.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/hosted-git-info": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz",
"integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==",
"dev": true,
"dependencies": {
"lru-cache": "^7.5.1"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/semantic-release/node_modules/human-signals": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz",
"integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==",
"dev": true,
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/semantic-release/node_modules/indent-string": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
"integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/is-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/locate-path": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
"integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
"dev": true,
"dependencies": {
"p-locate": "^6.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/lru-cache": {
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
"integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
"dev": true,
"engines": {
"node": ">=12"
}
},
"node_modules/semantic-release/node_modules/mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/npm-run-path": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
"integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
"dev": true,
"dependencies": {
"path-key": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/onetime": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
"dev": true,
"dependencies": {
"mimic-fn": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/p-limit": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
"integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
"dev": true,
"dependencies": {
"yocto-queue": "^1.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/p-locate": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
"integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
"dev": true,
"dependencies": {
"p-limit": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/p-reduce": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz",
"integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/path-exists": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
"integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
"node_modules/semantic-release/node_modules/path-key": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/read-pkg": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz",
"integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==",
"dev": true,
"dependencies": {
"@types/normalize-package-data": "^2.4.1",
"normalize-package-data": "^3.0.2",
"parse-json": "^5.2.0",
"type-fest": "^2.0.0"
},
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/read-pkg-up": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz",
"integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==",
"dev": true,
"dependencies": {
"find-up": "^6.3.0",
"read-pkg": "^7.1.0",
"type-fest": "^2.5.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/strip-final-newline": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semantic-release/node_modules/type-fest": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
"dev": true,
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semver": {
@@ -5450,33 +5917,27 @@
}
},
"node_modules/semver-diff": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz",
"integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz",
"integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==",
"dev": true,
"dependencies": {
"semver": "^6.3.0"
"semver": "^7.3.5"
},
"engines": {
"node": ">=8"
}
},
"node_modules/semver-diff/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semver-regex": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz",
"integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz",
"integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==",
"dev": true,
"engines": {
"node": ">=8"
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -6067,31 +6528,22 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"dev": true,
"engines": {
"node": ">= 6"
}
},
"node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"version": "17.6.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz",
"integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==",
"dev": true,
"dependencies": {
"cliui": "^7.0.2",
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=10"
"node": ">=12"
}
},
"node_modules/yargs-parser": {
@@ -6102,6 +6554,27 @@
"engines": {
"node": ">=10"
}
},
"node_modules/yargs/node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"engines": {
"node": ">=12"
}
},
"node_modules/yocto-queue": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
"integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==",
"dev": true,
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
}
}
}

View File

@@ -1,8 +1,9 @@
{
"devDependencies": {
"@saithodev/semantic-release-backmerge": "^3.1.0",
"@semantic-release/changelog": "^6.0.2",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.7.4",
"semantic-release": "^19.0.5"
"gradle-semantic-release-plugin": "^1.7.6",
"semantic-release": "^20.1.0"
}
}

View File

@@ -1,18 +1,13 @@
package app.revanced.patcher
import app.revanced.patcher.data.Context
import app.revanced.patcher.data.findIndexed
import app.revanced.patcher.extensions.PatchExtensions.dependencies
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.extensions.PatchExtensions.requiresIntegrations
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolveUsingLookupMap
import app.revanced.patcher.patch.*
import app.revanced.patcher.util.TypeUtil.traverseClassHierarchy
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,16 +21,15 @@ import brut.directory.ExtFile
import lanchon.multidexlib2.BasicDexFileNamer
import lanchon.multidexlib2.DexIO
import lanchon.multidexlib2.MultiDexIO
import org.jf.dexlib2.AccessFlags
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.Closeable
import java.io.File
import java.io.OutputStream
import java.nio.file.Files
private val NAMER = BasicDexFileNamer()
internal val NAMER = BasicDexFileNamer()
/**
* The ReVanced Patcher.
@@ -45,6 +39,7 @@ class Patcher(private val options: PatcherOptions) {
private val logger = options.logger
private val opcodes: Opcodes
private var resourceDecodingMode = ResourceDecodingMode.MANIFEST_ONLY
private var mergeIntegrations = false
val context: PatcherContext
companion object {
@@ -71,121 +66,19 @@ class Patcher(private val options: PatcherOptions) {
}
/**
* Add additional dex file container to the patcher.
* @param files The dex file containers to add to the patcher.
* @param process The callback for [files] which are being added.
* Add integrations to be merged by the patcher.
* The integrations will only be merged, if necessary.
*
* @param integrations The integrations, must be dex files or dex file container such as ZIP, APK or DEX files.
* @param callback The callback for [integrations] which are being added.
*/
fun addFiles(
files: List<File>,
process: (File) -> Unit
fun addIntegrations(
integrations: List<File>,
callback: (File) -> Unit
) {
with(context.bytecodeContext.classes) {
for (file in files) {
process(file)
for (classDef in MultiDexIO.readDexFile(true, file, NAMER, null, null).classes) {
val type = classDef.type
val result = classes.findIndexed { it.type == type }
if (result == null) {
logger.trace("Merging type $type")
classes.add(classDef)
} else {
val (existingClass, existingClassIndex) = result
logger.trace("Type $type exists. Adding missing methods and fields.")
/**
* 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())
}
/**
* Check if the [AccessFlags.PUBLIC] flag is set.
*
* @return True, if the flag is set.
*/
fun Int.isPublic() = AccessFlags.PUBLIC.isSet(this)
/**
* Make a class and its super class public recursively.
*/
fun MutableClass.publicize() {
context.bytecodeContext.traverseClassHierarchy(this) {
if (accessFlags.isPublic()) return@traverseClassHierarchy
accessFlags = accessFlags.or(AccessFlags.PUBLIC.value)
}
}
/**
* Add missing methods to the class, considering to publicise the [ClassDef] if necessary.
*/
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 {
// make sure the class is public, if the class contains public methods
if (missingMethods.any { it.accessFlags.isPublic() })
classDef.publicize()
methods.addAll(missingMethods)
}
}
}
}
/**
* Add missing fields to the class, considering to publicise the [ClassDef] if necessary.
*/
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 ->
// make sure the class is public, if the class contains public fields
if (missingFields.any { it.accessFlags.isPublic() })
classDef.publicize()
classDef.apply { fields.addAll(missingFields) }
}
}
}
classes[existingClassIndex] = addMissingMethods().addMissingFields()
.apply { if (!changed) return }
}
existingClass.addMissingFrom(classDef)
}
}
}
context.integrations.apply integrations@{
add(integrations)
this@integrations.callback = callback
}
}
@@ -274,18 +167,29 @@ class Patcher(private val options: PatcherOptions) {
*/
fun addPatches(patches: Iterable<Class<out Patch<Context>>>) {
/**
* Fill the cache with the instances of the [Patch]es for later use.
* Note: Dependencies of the [Patch] will be cached as well.
* Returns true if at least one patches or its dependencies matches the given predicate.
*/
fun Class<out Patch<Context>>.isResource() {
this.also {
if (!ResourcePatch::class.java.isAssignableFrom(it)) return@also
// set the mode to decode all resources before running the patches
fun Class<out Patch<Context>>.anyRecursively(predicate: (Class<out Patch<Context>>) -> Boolean): Boolean =
predicate(this) || dependencies?.any { it.java.anyRecursively(predicate) } == true
// Determine if resource patching is required.
for (patch in patches) {
if (patch.anyRecursively { ResourcePatch::class.java.isAssignableFrom(it) }) {
resourceDecodingMode = ResourceDecodingMode.FULL
}.dependencies?.forEach { it.java.isResource() }
break
}
}
context.patches.addAll(patches.onEach(Class<out Patch<Context>>::isResource))
// Determine if merging integrations is required.
for (patch in patches) {
if (patch.anyRecursively { it.requiresIntegrations }) {
mergeIntegrations = true
break
}
}
context.patches.addAll(patches)
}
/**
@@ -345,7 +249,13 @@ class Patcher(private val options: PatcherOptions) {
XmlPullStreamDecoder(
axmlParser, AndrolibResources().resXmlSerializer
).decodeManifest(
extInputFile.directory.getFileInput("AndroidManifest.xml"), nullOutputStream
extInputFile.directory.getFileInput("AndroidManifest.xml"),
// Older Android versions do not support OutputStream.nullOutputStream()
object : OutputStream() {
override fun write(b: Int) {
// do nothing
}
}
)
}
}
@@ -353,7 +263,7 @@ class Patcher(private val options: PatcherOptions) {
// read of the resourceTable which is created by reading the manifest file
context.packageMetadata.let { metadata ->
metadata.packageName = resourceTable.currentResPackage.name
metadata.packageVersion = resourceTable.versionInfo.versionName
metadata.packageVersion = resourceTable.versionInfo.versionName ?: resourceTable.versionInfo.versionCode
metadata.metaInfo.versionInfo = resourceTable.versionInfo
metadata.metaInfo.sdkInfo = resourceTable.sdkInfo
}
@@ -413,10 +323,7 @@ class Patcher(private val options: PatcherOptions) {
context.resourceContext
} else {
context.bytecodeContext.also { context ->
(patchInstance as BytecodePatch).fingerprints?.resolve(
context,
context.classes.classes
)
(patchInstance as BytecodePatch).fingerprints?.resolveUsingLookupMap(context)
}
}
@@ -434,31 +341,54 @@ class Patcher(private val options: PatcherOptions) {
}
return sequence {
if (mergeIntegrations) context.integrations.merge(logger)
logger.trace("Initialize lookup maps for method MethodFingerprint resolution")
MethodFingerprint.initializeFingerprintResolutionLookupMaps(context.bytecodeContext)
// prevent from decoding the manifest twice if it is not needed
if (resourceDecodingMode == ResourceDecodingMode.FULL) decodeResources(ResourceDecodingMode.FULL)
logger.trace("Executing all patches")
logger.info("Executing patches")
val executedPatches = LinkedHashMap<String, ExecutedPatch>() // first is name
try {
context.patches.forEach { patch ->
val patchResult = executePatch(patch, executedPatches)
context.patches.forEach { patch ->
val patchResult = executePatch(patch, executedPatches)
val result = if (patchResult.isSuccess()) {
Result.success(patchResult.success()!!)
} else {
Result.failure(patchResult.error()!!)
}
val result = if (patchResult.isSuccess()) {
Result.success(patchResult.success()!!)
} else {
Result.failure(patchResult.error()!!)
}
yield(patch.patchName to result)
if (stopOnError && patchResult.isError()) return@sequence
}
} finally {
executedPatches.values.reversed().forEach { (patch, _) ->
patch.close()
}
// TODO: This prints before the patch really finishes in case it is a Closeable
// because the Closeable is closed after all patches are executed.
yield(patch.patchName to result)
if (stopOnError && patchResult.isError()) return@sequence
}
executedPatches.values
.filter(ExecutedPatch::success)
.map(ExecutedPatch::patchInstance)
.filterIsInstance(Closeable::class.java)
.asReversed().forEach {
try {
it.close()
} catch (exception: Exception) {
val patchName = (it as Patch<Context>).javaClass.patchName
logger.error("Failed to close '$patchName': ${exception.stackTraceToString()}")
yield(patchName to Result.failure(exception))
// This is not failsafe. If a patch throws an exception while closing,
// the other patches that depend on it may fail.
if (stopOnError) return@sequence
}
}
}
}

View File

@@ -1,10 +1,9 @@
package app.revanced.patcher
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.Context
import app.revanced.patcher.data.PackageMetadata
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.data.*
import app.revanced.patcher.logging.Logger
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.util.ClassMerger.merge
import org.jf.dexlib2.iface.ClassDef
import java.io.File
@@ -14,6 +13,52 @@ data class PatcherContext(
) {
val packageMetadata = PackageMetadata()
internal val patches = mutableListOf<Class<out Patch<Context>>>()
internal val integrations = Integrations(this)
internal val bytecodeContext = BytecodeContext(classes)
internal val resourceContext = ResourceContext(resourceCacheDirectory)
internal class Integrations(val context: PatcherContext) {
var callback: ((File) -> Unit)? = null
private val integrations: MutableList<File> = mutableListOf()
fun add(integrations: List<File>) = this@Integrations.integrations.addAll(integrations)
/**
* Merge integrations.
* @param logger A logger.
*/
fun merge(logger: Logger) {
with(context.bytecodeContext.classes) {
for (integrations in integrations) {
callback?.let { it(integrations) }
for (classDef in lanchon.multidexlib2.MultiDexIO.readDexFile(
true,
integrations,
NAMER,
null,
null
).classes) {
val type = classDef.type
val result = classes.findIndexed { it.type == type }
if (result == null) {
logger.trace("Merging type $type")
classes.add(classDef)
continue
}
val (existingClass, existingClassIndex) = result
logger.trace("Type $type exists. Adding missing methods and fields.")
existingClass.merge(classDef, context, logger).let { mergedClass ->
if (mergedClass !== existingClass) // referential equality check
classes[existingClassIndex] = mergedClass
}
}
}
}
}
}
}

View File

@@ -7,8 +7,6 @@ import app.revanced.patcher.patch.Patch
* @param compatiblePackages A list of packages a [Patch] is compatible with.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Compatibility(
val compatiblePackages: Array<Package>,
)
@@ -19,8 +17,6 @@ annotation class Compatibility(
* @param versions The versions of the package the [Patch] is compatible with.
*/
@Target()
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Package(
val name: String,
val versions: Array<String> = [],

View File

@@ -7,8 +7,6 @@ import app.revanced.patcher.patch.Patch
* @param name A suggestive name for the [Patch].
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Name(
val name: String,
)
@@ -18,8 +16,6 @@ annotation class Name(
* @param description A description for the [Patch].
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Description(
val description: String,
)
@@ -30,8 +26,6 @@ annotation class Description(
* @param version The version of a [Patch].
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Version(
val version: String,
)

View File

@@ -54,17 +54,6 @@ class BytecodeContext internal constructor(classes: MutableList<ClassDef>) : Con
}
return proxy
}
private companion object {
inline fun <reified T> Iterable<T>.find(predicate: (T) -> Boolean): T? {
for (element in this) {
if (predicate(element)) {
return element
}
}
return null
}
}
}
/**

View File

@@ -1,79 +1,33 @@
package app.revanced.patcher.extensions
import app.revanced.patcher.annotation.*
import app.revanced.patcher.data.Context
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.patch.OptionsContainer
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchOptions
import app.revanced.patcher.patch.annotations.DependsOn
import kotlin.reflect.KClass
import kotlin.reflect.KVisibility
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.companionObjectInstance
/**
* Recursively find a given annotation on a class.
* @param targetAnnotation The annotation to find.
* @return The annotation.
*/
private fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: KClass<T>) =
this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf())
internal object AnnotationExtensions {
/**
* Recursively find a given annotation on a class.
*
* @param targetAnnotation The annotation to find.
* @return The annotation.
*/
fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: KClass<T>): T? {
fun <T : Annotation> Class<*>.findAnnotationRecursively(
targetAnnotation: Class<T>, traversed: MutableSet<Annotation>
): T? {
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }
@Suppress("UNCHECKED_CAST") if (found != null) return found as T
private fun <T : Annotation> Class<*>.findAnnotationRecursively(
targetAnnotation: Class<T>, traversed: MutableSet<Annotation>
): T? {
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }
for (annotation in this.annotations) {
if (traversed.contains(annotation)) continue
traversed.add(annotation)
@Suppress("UNCHECKED_CAST") if (found != null) return found as T
for (annotation in this.annotations) {
if (traversed.contains(annotation)) continue
traversed.add(annotation)
return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed)) ?: continue
}
return null
}
object PatchExtensions {
val Class<out Patch<Context>>.patchName: String
get() = findAnnotationRecursively(Name::class)?.name ?: this.simpleName
val Class<out Patch<Context>>.version
get() = findAnnotationRecursively(Version::class)?.version
val Class<out Patch<Context>>.include
get() = findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class)!!.include
val Class<out Patch<Context>>.description
get() = findAnnotationRecursively(Description::class)?.description
val Class<out Patch<Context>>.dependencies
get() = findAnnotationRecursively(DependsOn::class)?.dependencies
val Class<out Patch<Context>>.compatiblePackages
get() = findAnnotationRecursively(Compatibility::class)?.compatiblePackages
val Class<out Patch<Context>>.options: PatchOptions?
get() = kotlin.companionObject?.let { cl ->
if (cl.visibility != KVisibility.PUBLIC) return null
kotlin.companionObjectInstance?.let {
(it as? OptionsContainer)?.options
return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed))
?: continue
}
return null
}
}
object MethodFingerprintExtensions {
val MethodFingerprint.name: String
get() = this.javaClass.simpleName
val MethodFingerprint.fuzzyPatternScanMethod
get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class)
val MethodFingerprint.fuzzyScanThreshold
get() = fuzzyPatternScanMethod?.threshold ?: 0
return this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf())
}
}

View File

@@ -1,247 +1,33 @@
package app.revanced.patcher.extensions
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patcher.util.smali.toInstructions
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.builder.BuilderInstruction
import org.jf.dexlib2.builder.BuilderOffsetInstruction
import org.jf.dexlib2.builder.Label
import org.jf.dexlib2.builder.MutableMethodImplementation
import org.jf.dexlib2.builder.instruction.*
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.immutable.ImmutableMethod
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
import java.io.OutputStream
infix fun AccessFlags.or(other: AccessFlags) = this.value or other.value
infix fun Int.or(other: AccessFlags) = this or other.value
fun MutableMethodImplementation.addInstructions(index: Int, instructions: List<BuilderInstruction>) {
for (i in instructions.lastIndex downTo 0) {
this.addInstruction(index, instructions[i])
}
}
fun MutableMethodImplementation.addInstructions(instructions: List<BuilderInstruction>) {
for (instruction in instructions) {
this.addInstruction(instruction)
}
}
fun MutableMethodImplementation.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) {
for (i in instructions.lastIndex downTo 0) {
this.replaceInstruction(index + i, instructions[i])
}
}
fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) {
for (i in count downTo 0) {
this.removeInstruction(index + i)
}
}
/**
* Clones the method.
* @param registerCount This parameter allows you to change the register count of the method.
* This may be a positive or negative number.
* @return The **immutable** cloned method. Call [toMutable] or [cloneMutable] to get a **mutable** copy.
*/
internal fun Method.clone(registerCount: Int = 0): ImmutableMethod {
val clonedImplementation = implementation?.let {
ImmutableMethodImplementation(
it.registerCount + registerCount,
it.instructions,
it.tryBlocks,
it.debugItems,
)
}
return ImmutableMethod(
returnType, name, parameters, returnType, accessFlags, annotations, hiddenApiRestrictions, clonedImplementation
)
}
/**
* Add a smali instruction to the method.
* @param instruction The smali instruction to add.
*/
fun MutableMethod.addInstruction(instruction: String) =
this.implementation!!.addInstruction(instruction.toInstruction(this))
/**
* Add a smali instruction to the method.
* @param index The index to insert the instruction at.
* @param instruction The smali instruction to add.
*/
fun MutableMethod.addInstruction(index: Int, instruction: String) =
this.implementation!!.addInstruction(index, instruction.toInstruction(this))
/**
* Replace a smali instruction within the method.
* @param index The index to replace the instruction at.
* @param instruction The smali instruction to place.
*/
fun MutableMethod.replaceInstruction(index: Int, instruction: String) =
this.implementation!!.replaceInstruction(index, instruction.toInstruction(this))
/**
* Remove a smali instruction within the method.
* @param index The index to delete the instruction at.
*/
fun MutableMethod.removeInstruction(index: Int) = this.implementation!!.removeInstruction(index)
/**
* Create a label for the instruction at given index in the method's implementation.
* Create a label for the instruction at given index.
*
* @param index The index to create the label for the instruction at.
* @return The label.
*/
fun MutableMethod.label(index: Int) = this.implementation!!.newLabelForIndex(index)
fun MutableMethod.newLabel(index: Int) = implementation!!.newLabelForIndex(index)
/**
* Get the instruction at given index in the method's implementation.
* @param index The index to get the instruction at.
* @return The instruction.
* Perform a bitwise OR operation between two [AccessFlags].
*
* @param other The other [AccessFlags] to perform the operation with.
*/
fun MutableMethod.instruction(index: Int): BuilderInstruction = this.implementation!!.instructions[index]
infix fun AccessFlags.or(other: AccessFlags) = value or other.value
/**
* Add smali instructions to the method.
* @param index The index to insert the instructions at.
* @param smali The smali instructions to add.
* @param externalLabels A list of [ExternalLabel] representing a list of labels for instructions which are not in the method to compile.
* Perform a bitwise OR operation between an [AccessFlags] and an [Int].
*
* @param other The [Int] to perform the operation with.
*/
fun MutableMethod.addInstructions(index: Int, smali: String, externalLabels: List<ExternalLabel> = emptyList()) {
// Create reference dummy instructions for the instructions.
val nopedSmali = StringBuilder(smali).also { builder ->
externalLabels.forEach { (name, _) ->
builder.append("\n:$name\nnop")
}
}.toString()
// Compile the instructions with the dummy labels
val compiledInstructions = nopedSmali.toInstructions(this)
// Add the compiled list of instructions to the method.
val methodImplementation = this.implementation!!
methodImplementation.addInstructions(index, compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size))
val methodInstructions = methodImplementation.instructions
methodInstructions.subList(index, index + compiledInstructions.size - externalLabels.size)
.forEachIndexed { compiledInstructionIndex, compiledInstruction ->
// If the compiled instruction is not an offset instruction, skip it.
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
/**
* Creates a new label for the instruction and replaces it with the label of the [compiledInstruction] at [compiledInstructionIndex].
*/
fun Instruction.makeNewLabel() {
// Create the final label.
val label = methodImplementation.newLabelForIndex(methodInstructions.indexOf(this))
// Create the final instruction with the new label.
val newInstruction = replaceOffset(
compiledInstruction, label
)
// Replace the instruction pointing to the dummy label with the new instruction pointing to the real instruction.
methodImplementation.replaceInstruction(index + compiledInstructionIndex, newInstruction)
}
// If the compiled instruction targets its own instruction,
// which means it points to some of its own, simply an offset has to be applied.
val labelIndex = compiledInstruction.target.location.index
if (labelIndex < compiledInstructions.size - externalLabels.size) {
// Get the targets index (insertion index + the index of the dummy instruction).
methodInstructions[index + labelIndex].makeNewLabel()
return@forEachIndexed
}
// Since the compiled instruction points to a dummy instruction,
// we can find the real instruction which it was created for by calculation.
// Get the index of the instruction in the externalLabels list which the dummy instruction was created for.
// this line works because we created the dummy instructions in the same order as the externalLabels list.
val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex]
instruction.makeNewLabel()
}
}
infix fun Int.or(other: AccessFlags) = this or other.value
/**
* Add smali instructions to the end of the method.
* @param instructions The smali instructions to add.
* Perform a bitwise OR operation between an [Int] and an [AccessFlags].
*
* @param other The [AccessFlags] to perform the operation with.
*/
fun MutableMethod.addInstructions(instructions: String, labels: List<ExternalLabel> = emptyList()) =
this.addInstructions(this.implementation!!.instructions.size, instructions, labels)
/**
* Replace smali instructions within the method.
* @param index The index to replace the instructions at.
* @param instructions The smali instructions to place.
*/
fun MutableMethod.replaceInstructions(index: Int, instructions: String) =
this.implementation!!.replaceInstructions(index, instructions.toInstructions(this))
/**
* Remove smali instructions from the method.
* @param index The index to remove the instructions at.
* @param count The amount of instructions to remove.
*/
fun MutableMethod.removeInstructions(index: Int, count: Int) = this.implementation!!.removeInstructions(index, count)
private fun replaceOffset(
i: BuilderOffsetInstruction, label: Label
): BuilderOffsetInstruction {
return when (i) {
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
is BuilderInstruction22t -> BuilderInstruction22t(i.opcode, i.registerA, i.registerB, label)
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
else -> throw IllegalStateException("A non-offset instruction was given, this should never happen!")
}
}
/**
* Clones the method.
* @param registerCount This parameter allows you to change the register count of the method.
* This may be a positive or negative number.
* @return The **mutable** cloned method. Call [clone] to get an **immutable** copy.
*/
internal fun Method.cloneMutable(registerCount: Int = 0) = clone(registerCount).toMutable()
// FIXME: also check the order of parameters as different order equals different method overload
internal fun parametersEqual(
parameters1: Iterable<CharSequence>, parameters2: Iterable<CharSequence>
): Boolean {
return parameters1.count() == parameters2.count() && parameters1.all { parameter ->
parameters2.any {
it.startsWith(
parameter
)
}
}
}
internal val nullOutputStream = object : OutputStream() {
override fun write(b: Int) {}
}
/**
* Should be used to parse a list of parameters represented by their first letter,
* or in the case of arrays prefixed with an unspecified amount of '[' character.
*/
internal fun String.parseParameters(): List<String> {
val parameters = mutableListOf<String>()
var parameter = ""
for (char in this.toCharArray()) {
parameter += char
if (char == '[') continue
parameters.add(parameter)
parameter = ""
}
return parameters
}
infix fun AccessFlags.or(other: Int) = value or other

View File

@@ -0,0 +1,326 @@
package app.revanced.patcher.extensions
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patcher.util.smali.toInstructions
import org.jf.dexlib2.builder.BuilderInstruction
import org.jf.dexlib2.builder.BuilderOffsetInstruction
import org.jf.dexlib2.builder.Label
import org.jf.dexlib2.builder.MutableMethodImplementation
import org.jf.dexlib2.builder.instruction.*
import org.jf.dexlib2.iface.instruction.Instruction
object InstructionExtensions {
/**
* Add instructions to a method at the given index.
*
* @param index The index to add the instructions at.
* @param instructions The instructions to add.
*/
fun MutableMethodImplementation.addInstructions(
index: Int,
instructions: List<BuilderInstruction>
) =
instructions.asReversed().forEach { addInstruction(index, it) }
/**
* Add instructions to a method.
* The instructions will be added at the end of the method.
*
* @param instructions The instructions to add.
*/
fun MutableMethodImplementation.addInstructions(instructions: List<BuilderInstruction>) =
instructions.forEach { this.addInstruction(it) }
/**
* Remove instructions from a method at the given index.
*
* @param index The index to remove the instructions at.
* @param count The amount of instructions to remove.
*/
fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) = repeat(count) {
removeInstruction(index)
}
/**
* Remove the first instructions from a method.
*
* @param count The amount of instructions to remove.
*/
fun MutableMethodImplementation.removeInstructions(count: Int) = removeInstructions(0, count)
/**
* Replace instructions at the given index with the given instructions.
* The amount of instructions to replace is the amount of instructions in the given list.
*
* @param index The index to replace the instructions at.
* @param instructions The instructions to replace the instructions with.
*/
fun MutableMethodImplementation.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) {
// Remove the instructions at the given index.
removeInstructions(index, instructions.size)
// Add the instructions at the given index.
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!!.addInstruction(index, smaliInstructions.toInstruction(this))
/**
* Add an instruction to a method.
*
* @param smaliInstructions The instruction to add.
*/
fun MutableMethod.addInstruction(smaliInstructions: String) =
implementation!!.addInstruction(smaliInstructions.toInstruction(this))
/**
* Add instructions to a method at the given index.
*
* @param index The index to add the instructions at.
* @param instructions The instructions to add.
*/
fun MutableMethod.addInstructions(index: Int, instructions: List<BuilderInstruction>) =
implementation!!.addInstructions(index, instructions)
/**
* Add instructions to a method.
*
* @param instructions The instructions to add.
*/
fun MutableMethod.addInstructions(instructions: List<BuilderInstruction>) =
implementation!!.addInstructions(instructions)
/**
* Add instructions to a method.
*
* @param smaliInstructions The instructions to add.
*/
fun MutableMethod.addInstructions(index: Int, smaliInstructions: String) =
implementation!!.addInstructions(index, smaliInstructions.toInstructions(this))
/**
* Add instructions to a method.
*
* @param smaliInstructions The instructions to add.
*/
fun MutableMethod.addInstructions(smaliInstructions: String) =
implementation!!.addInstructions(smaliInstructions.toInstructions(this))
/**
* Add instructions to a method at the given index.
*
* @param index The index to add the instructions at.
* @param smaliInstructions The instructions to add.
* @param externalLabels A list of [ExternalLabel] for instructions outside of [smaliInstructions].
*/
// Special function for adding instructions with external labels.
fun MutableMethod.addInstructionsWithLabels(
index: Int,
smaliInstructions: String,
vararg externalLabels: ExternalLabel
) {
// Create reference dummy instructions for the instructions.
val nopSmali = StringBuilder(smaliInstructions).also { builder ->
externalLabels.forEach { (name, _) ->
builder.append("\n:$name\nnop")
}
}.toString()
// Compile the instructions with the dummy labels
val compiledInstructions = nopSmali.toInstructions(this)
// Add the compiled list of instructions to the method.
addInstructions(
index,
compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size)
)
implementation!!.apply {
this@apply.instructions.subList(index, index + compiledInstructions.size - externalLabels.size)
.forEachIndexed { compiledInstructionIndex, compiledInstruction ->
// If the compiled instruction is not an offset instruction, skip it.
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
/**
* Creates a new label for the instruction
* and replaces it with the label of the [compiledInstruction] at [compiledInstructionIndex].
*/
fun Instruction.makeNewLabel() {
fun replaceOffset(
i: BuilderOffsetInstruction, label: Label
): BuilderOffsetInstruction {
return when (i) {
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
is BuilderInstruction22t -> BuilderInstruction22t(
i.opcode,
i.registerA,
i.registerB,
label
)
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
else -> throw IllegalStateException(
"A non-offset instruction was given, this should never happen!"
)
}
}
// Create the final label.
val label = newLabelForIndex(this@apply.instructions.indexOf(this))
// Create the final instruction with the new label.
val newInstruction = replaceOffset(
compiledInstruction, label
)
// Replace the instruction pointing to the dummy label
// with the new instruction pointing to the real instruction.
replaceInstruction(index + compiledInstructionIndex, newInstruction)
}
// If the compiled instruction targets its own instruction,
// which means it points to some of its own, simply an offset has to be applied.
val labelIndex = compiledInstruction.target.location.index
if (labelIndex < compiledInstructions.size - externalLabels.size) {
// Get the targets index (insertion index + the index of the dummy instruction).
this.instructions[index + labelIndex].makeNewLabel()
return@forEachIndexed
}
// Since the compiled instruction points to a dummy instruction,
// we can find the real instruction which it was created for by calculation.
// Get the index of the instruction in the externalLabels list
// which the dummy instruction was created for.
// This works because we created the dummy instructions in the same order as the externalLabels list.
val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex]
instruction.makeNewLabel()
}
}
}
/**
* 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.
*
* @param index The index to remove the instructions at.
* @param count The amount of instructions to remove.
*/
fun MutableMethod.removeInstructions(index: Int, count: Int) =
implementation!!.removeInstructions(index, count)
/**
* Remove instructions at the given index.
*
* @param count The amount of instructions to remove.
*/
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!!.replaceInstruction(index, smaliInstruction.toInstruction(this))
/**
* Replace instructions at the given index.
*
* @param index The index to replace the instructions at.
* @param instructions The instructions to replace the instructions with.
*/
fun MutableMethod.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) =
implementation!!.replaceInstructions(index, instructions)
/**
* Replace instructions at the given index.
*
* @param index The index to replace the instructions at.
* @param smaliInstructions The smali instructions to replace the instructions with.
*/
fun MutableMethod.replaceInstructions(index: Int, smaliInstructions: String) =
implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this))
/**
* Get an instruction at the given index.
*
* @param index The index to get the instruction at.
* @return The instruction.
*/
fun MutableMethodImplementation.getInstruction(index: Int): BuilderInstruction = instructions[index]
/**
* Get an instruction at the given index.
*
* @param index The index to get the instruction at.
* @param T The type of instruction to return.
* @return The instruction.
*/
@Suppress("UNCHECKED_CAST")
fun <T> MutableMethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T
/**
* Get an instruction at the given index.
* @param index The index to get the instruction at.
* @return The instruction.
*/
fun MutableMethod.getInstruction(index: Int): BuilderInstruction = implementation!!.getInstruction(index)
/**
* Get an instruction at the given index.
* @param index The index to get the instruction at.
* @param T The type of instruction to return.
* @return The instruction.
*/
@Suppress("UNCHECKED_CAST")
fun <T> MutableMethod.getInstruction(index: Int): T = implementation!!.getInstruction<T>(index)
}

View File

@@ -0,0 +1,20 @@
package app.revanced.patcher.extensions
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object MethodFingerprintExtensions {
/**
* The name of a [MethodFingerprint].
*/
val MethodFingerprint.name: String
get() = this.javaClass.simpleName
/**
* The [FuzzyPatternScanMethod] annotation of a [MethodFingerprint].
*/
val MethodFingerprint.fuzzyPatternScanMethod
get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class)
}

View File

@@ -0,0 +1,71 @@
package app.revanced.patcher.extensions
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.Context
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
import app.revanced.patcher.patch.OptionsContainer
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchOptions
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.RequiresIntegrations
import kotlin.reflect.KVisibility
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.companionObjectInstance
object PatchExtensions {
/**
* The name of a [Patch].
*/
val Class<out Patch<Context>>.patchName: String
get() = findAnnotationRecursively(Name::class)?.name ?: this.simpleName
/**
* The version of a [Patch].
*/
val Class<out Patch<Context>>.version
get() = findAnnotationRecursively(Version::class)?.version
/**
* Weather or not a [Patch] should be included.
*/
val Class<out Patch<Context>>.include
get() = findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class)!!.include
/**
* The description of a [Patch].
*/
val Class<out Patch<Context>>.description
get() = findAnnotationRecursively(Description::class)?.description
/**
* The dependencies of a [Patch].
*/
val Class<out Patch<Context>>.dependencies
get() = findAnnotationRecursively(DependsOn::class)?.dependencies
/**
* The packages a [Patch] is compatible with.
*/
val Class<out Patch<Context>>.compatiblePackages
get() = findAnnotationRecursively(Compatibility::class)?.compatiblePackages
/**
* Weather or not a [Patch] requires integrations.
*/
internal val Class<out Patch<Context>>.requiresIntegrations
get() = findAnnotationRecursively(RequiresIntegrations::class) != null
/**
* The options of a [Patch].
*/
val Class<out Patch<Context>>.options: PatchOptions?
get() = kotlin.companionObject?.let { cl ->
if (cl.visibility != KVisibility.PUBLIC) return null
kotlin.companionObjectInstance?.let {
(it as? OptionsContainer)?.options
}
}
}

View File

@@ -7,7 +7,6 @@ import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
* @param threshold if [threshold] or more of the opcodes do not match, skip.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class FuzzyPatternScanMethod(
val threshold: Int = 1
)

View File

@@ -2,11 +2,11 @@ package app.revanced.patcher.fingerprint.method.impl
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyScanThreshold
import app.revanced.patcher.extensions.parametersEqual
import app.revanced.patcher.fingerprint.Fingerprint
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.util.proxy.ClassProxy
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.Method
@@ -14,31 +14,222 @@ import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.StringReference
import org.jf.dexlib2.util.MethodUtil
import java.util.*
private typealias StringMatch = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult.StringMatch
private typealias StringsScanResult = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult
private typealias MethodClassPair = Pair<Method, ClassDef>
/**
* Represents the [MethodFingerprint] for a method.
* @param returnType The return type of the method.
* @param access The access flags of the method.
* @param parameters The parameters of the method.
* @param opcodes The list of opcodes of the method.
* @param strings A list of strings which a method contains.
* A fingerprint to resolve methods.
*
* @param returnType The method's return type compared using [String.startsWith].
* @param accessFlags The method's exact access flags using values of [AccessFlags].
* @param parameters The parameters of the method. Partial matches allowed and follow the same rules as [returnType].
* @param opcodes An opcode pattern of the method's instructions. Wildcard or unknown opcodes can be specified by `null`.
* @param strings A list of the method's strings compared each using [String.contains].
* @param customFingerprint A custom condition for this fingerprint.
* A `null` opcode is equals to an unknown opcode.
*/
abstract class MethodFingerprint(
internal val returnType: String? = null,
internal val access: Int? = null,
internal val accessFlags: Int? = null,
internal val parameters: Iterable<String>? = null,
internal val opcodes: Iterable<Opcode?>? = null,
internal val strings: Iterable<String>? = null,
internal val customFingerprint: ((methodDef: Method) -> Boolean)? = null
internal val customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null
) : Fingerprint {
/**
* The result of the [MethodFingerprint] the [Method].
* The result of the [MethodFingerprint].
*/
var result: MethodFingerprintResult? = null
companion object {
/**
* A list of methods and the class they were found in.
*/
private val methods = mutableListOf<MethodClassPair>()
/**
* Lookup map for methods keyed to the methods access flags, return type and parameter.
*/
private val methodSignatureLookupMap = mutableMapOf<String, MutableList<MethodClassPair>>()
/**
* Lookup map for methods keyed to the strings contained in the method.
*/
private val methodStringsLookupMap = mutableMapOf<String, MutableList<MethodClassPair>>()
/**
* Appends a string based on the parameter reference types of this method.
*/
private fun StringBuilder.appendParameters(parameters: Iterable<CharSequence>) {
// Maximum parameters to use in the signature key.
// Some apps have methods with an incredible number of parameters (over 100 parameters have been seen).
// To keep the signature map from becoming needlessly bloated,
// group together in the same map entry all methods with the same access/return and 5 or more parameters.
// The value of 5 was chosen based on local performance testing and is not set in stone.
val maxSignatureParameters = 5
// Must append a unique value before the parameters to distinguish this key includes the parameters.
// If this is not appended, then methods with no parameters
// will collide with different keys that specify access/return but omit the parameters.
append("p:")
parameters.forEachIndexed { index, parameter ->
if (index >= maxSignatureParameters) return
append(parameter.first())
}
}
/**
* Initializes lookup maps for [MethodFingerprint] resolution
* using attributes of methods such as the method signature or strings.
*
* @param context The [BytecodeContext] containing the classes to initialize the lookup maps with.
*/
internal fun initializeFingerprintResolutionLookupMaps(context: BytecodeContext) {
fun MutableMap<String, MutableList<MethodClassPair>>.add(
key: String,
methodClassPair: MethodClassPair
) {
var methodClassPairs = this[key]
methodClassPairs ?: run {
methodClassPairs = LinkedList<MethodClassPair>().also { this[key] = it }
}
methodClassPairs!!.add(methodClassPair)
}
if (methods.isNotEmpty()) throw PatchResultError("Map already initialized")
context.classes.classes.forEach { classDef ->
classDef.methods.forEach { method ->
val methodClassPair = method to classDef
// For fingerprints with no access or return type specified.
methods += methodClassPair
val accessFlagsReturnKey = method.accessFlags.toString() + method.returnType.first()
// Add <access><returnType> as the key.
methodSignatureLookupMap.add(accessFlagsReturnKey, methodClassPair)
// Add <access><returnType>[parameters] as the key.
methodSignatureLookupMap.add(
buildString {
append(accessFlagsReturnKey)
appendParameters(method.parameterTypes)
},
methodClassPair
)
// Add strings contained in the method as the key.
method.implementation?.instructions?.forEach instructions@{ instruction ->
if (instruction.opcode != Opcode.CONST_STRING && instruction.opcode != Opcode.CONST_STRING_JUMBO)
return@instructions
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
methodStringsLookupMap.add(string, methodClassPair)
}
// In the future, the class type could be added to the lookup map.
// This would require MethodFingerprint to be changed to include the class type.
}
}
}
/**
* Resolve a list of [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps].
*
* [MethodFingerprint] resolution is fast, but if many are present they can consume a noticeable
* amount of time because they are resolved in sequence.
*
* For apps with many fingerprints, resolving performance can be improved by:
* - Slowest: Specify [opcodes] and nothing else.
* - Fast: Specify [accessFlags], [returnType].
* - Faster: Specify [accessFlags], [returnType] and [parameters].
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
*/
internal fun Iterable<MethodFingerprint>.resolveUsingLookupMap(context: BytecodeContext) {
if (methods.isEmpty()) throw PatchResultError("lookup map not initialized")
for (fingerprint in this) {
fingerprint.resolveUsingLookupMap(context)
}
}
/**
* Resolve a [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps].
*
* [MethodFingerprint] resolution is fast, but if many are present they can consume a noticeable
* amount of time because they are resolved in sequence.
*
* For apps with many fingerprints, resolving performance can be improved by:
* - Slowest: Specify [opcodes] and nothing else.
* - Fast: Specify [accessFlags], [returnType].
* - Faster: Specify [accessFlags], [returnType] and [parameters].
* - Fastest: Specify [strings], with at least one string being an exact (non-partial) match.
*/
internal fun MethodFingerprint.resolveUsingLookupMap(context: BytecodeContext): Boolean {
/**
* Lookup [MethodClassPair]s that match the methods strings present in a [MethodFingerprint].
*
* @return A list of [MethodClassPair]s that match the methods strings present in a [MethodFingerprint].
*/
fun MethodFingerprint.methodStringsLookup(): List<MethodClassPair>? {
strings?.forEach {
val methods = methodStringsLookupMap[it]
if (methods != null) return methods
}
return null
}
/**
* Lookup [MethodClassPair]s that match the method signature present in a [MethodFingerprint].
*
* @return A list of [MethodClassPair]s that match the method signature present in a [MethodFingerprint].
*/
fun MethodFingerprint.methodSignatureLookup(): List<MethodClassPair> {
if (accessFlags == null) return methods
var returnTypeValue = returnType
if (returnTypeValue == null) {
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
// Constructors always have void return type
returnTypeValue = "V"
} else {
return methods
}
}
val key = buildString {
append(accessFlags)
append(returnTypeValue.first())
if (parameters != null) appendParameters(parameters)
}
return methodSignatureLookupMap[key] ?: return emptyList()
}
/**
* Resolve a [MethodFingerprint] using a list of [MethodClassPair].
*
* @return True if the resolution was successful, false otherwise.
*/
fun MethodFingerprint.resolveUsingMethodClassPair(classMethods: Iterable<MethodClassPair>): Boolean {
classMethods.forEach { classAndMethod ->
if (resolve(context, classAndMethod.first, classAndMethod.second)) return true
}
return false
}
val methodsWithSameStrings = methodStringsLookup()
if (methodsWithSameStrings != null) if (resolveUsingMethodClassPair(methodsWithSameStrings)) return true
// No strings declared or none matched (partial matches are allowed).
// Use signature matching.
return resolveUsingMethodClassPair(methodSignatureLookup())
}
/**
* Resolve a list of [MethodFingerprint] against a list of [ClassDef].
*
@@ -47,10 +238,10 @@ abstract class MethodFingerprint(
* @return True if the resolution was successful, false otherwise.
*/
fun Iterable<MethodFingerprint>.resolve(context: BytecodeContext, classes: Iterable<ClassDef>) {
for (fingerprint in this) // For each fingerprint
classes@ for (classDef in classes) // search through all classes for the fingerprint
for (fingerprint in this) // For each fingerprint...
classes@ for (classDef in classes) // ...search through all classes for the MethodFingerprint
if (fingerprint.resolve(context, classDef))
break@classes // if the resolution succeeded, continue with the next fingerprint
break@classes // ...if the resolution succeeded, continue with the next MethodFingerprint.
}
/**
@@ -83,10 +274,21 @@ abstract class MethodFingerprint(
if (methodFingerprint.returnType != null && !method.returnType.startsWith(methodFingerprint.returnType))
return false
if (methodFingerprint.access != null && methodFingerprint.access != method.accessFlags)
if (methodFingerprint.accessFlags != null && methodFingerprint.accessFlags != method.accessFlags)
return false
fun parametersEqual(
parameters1: Iterable<CharSequence>, parameters2: Iterable<CharSequence>
): Boolean {
if (parameters1.count() != parameters2.count()) return false
val iterator1 = parameters1.iterator()
parameters2.forEach {
if (!it.startsWith(iterator1.next())) return false
}
return true
}
if (methodFingerprint.parameters != null && !parametersEqual(
methodFingerprint.parameters, // TODO: parseParameters()
method.parameterTypes
@@ -94,7 +296,7 @@ abstract class MethodFingerprint(
) return false
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
if (methodFingerprint.customFingerprint != null && !methodFingerprint.customFingerprint!!(method))
if (methodFingerprint.customFingerprint != null && !methodFingerprint.customFingerprint!!(method, forClass))
return false
val stringsScanResult: StringsScanResult? =
@@ -106,7 +308,10 @@ abstract class MethodFingerprint(
val stringsList = methodFingerprint.strings.toMutableList()
implementation.instructions.forEachIndexed { instructionIndex, instruction ->
if (instruction.opcode.ordinal != Opcode.CONST_STRING.ordinal) return@forEachIndexed
if (
instruction.opcode != Opcode.CONST_STRING &&
instruction.opcode != Opcode.CONST_STRING_JUMBO
) return@forEachIndexed
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
val index = stringsList.indexOfFirst(string::contains)
@@ -129,6 +334,52 @@ abstract class MethodFingerprint(
val patternScanResult = if (methodFingerprint.opcodes != null) {
method.implementation?.instructions ?: return false
fun Method.patternScan(
fingerprint: MethodFingerprint
): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? {
val instructions = this.implementation!!.instructions
val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0
val pattern = fingerprint.opcodes!!
val instructionLength = instructions.count()
val patternLength = pattern.count()
for (index in 0 until instructionLength) {
var patternIndex = 0
var threshold = fingerprintFuzzyPatternScanThreshold
while (index + patternIndex < instructionLength) {
val originalOpcode = instructions.elementAt(index + patternIndex).opcode
val patternOpcode = pattern.elementAt(patternIndex)
if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) {
// reaching maximum threshold (0) means,
// the pattern does not match to the current instructions
if (threshold-- == 0) break
}
if (patternIndex < patternLength - 1) {
// if the entire pattern has not been scanned yet
// continue the scan
patternIndex++
continue
}
// the pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod
val result =
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult(
index,
index + patternIndex
)
if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result
result.warnings = result.createWarnings(pattern, instructions)
return result
}
}
return null
}
method.patternScan(methodFingerprint) ?: return false
} else null
@@ -145,52 +396,6 @@ abstract class MethodFingerprint(
return true
}
private fun Method.patternScan(
fingerprint: MethodFingerprint
): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? {
val instructions = this.implementation!!.instructions
val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyScanThreshold
val pattern = fingerprint.opcodes!!
val instructionLength = instructions.count()
val patternLength = pattern.count()
for (index in 0 until instructionLength) {
var patternIndex = 0
var threshold = fingerprintFuzzyPatternScanThreshold
while (index + patternIndex < instructionLength) {
val originalOpcode = instructions.elementAt(index + patternIndex).opcode
val patternOpcode = pattern.elementAt(patternIndex)
if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) {
// reaching maximum threshold (0) means,
// the pattern does not match to the current instructions
if (threshold-- == 0) break
}
if (patternIndex < patternLength - 1) {
// if the entire pattern has not been scanned yet
// continue the scan
patternIndex++
continue
}
// the pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod
val result =
MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult(
index,
index + patternIndex
)
if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result
result.warnings = result.createWarnings(pattern, instructions)
return result
}
}
return null
}
private fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.createWarnings(
pattern: Iterable<Opcode?>, instructions: Iterable<Instruction>
) = buildList {
@@ -213,9 +418,6 @@ abstract class MethodFingerprint(
}
}
private typealias StringMatch = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult.StringMatch
private typealias StringsScanResult = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult
/**
* Represents the result of a [MethodFingerprintResult].
*
@@ -230,6 +432,26 @@ data class MethodFingerprintResult(
val scanResult: MethodFingerprintScanResult,
internal val context: BytecodeContext
) {
/**
* Returns a mutable clone of [classDef]
*
* Please note, this method allocates a [ClassProxy].
* Use [classDef] where possible.
*/
@Suppress("MemberVisibilityCanBePrivate")
val mutableClass by lazy { context.proxy(classDef).mutableClass }
/**
* Returns a mutable clone of [method]
*
* Please note, this method allocates a [ClassProxy].
* Use [method] where possible.
*/
val mutableMethod by lazy {
mutableClass.methods.first {
MethodUtil.methodSignaturesMatch(it, this.method)
}
}
/**
* The result of scanning on the [MethodFingerprint].
@@ -244,7 +466,7 @@ data class MethodFingerprintResult(
* The result of scanning strings on the [MethodFingerprint].
* @param matches The list of strings that were matched.
*/
data class StringsScanResult(val matches: List<StringMatch>){
data class StringsScanResult(val matches: List<StringMatch>) {
/**
* Represents a match for a string at an index.
* @param string The string that was matched.
@@ -279,25 +501,4 @@ data class MethodFingerprintResult(
)
}
}
/**
* Returns a mutable clone of [classDef]
*
* Please note, this method allocates a [ClassProxy].
* Use [classDef] where possible.
*/
@Suppress("MemberVisibilityCanBePrivate")
val mutableClass by lazy { context.proxy(classDef).mutableClass }
/**
* Returns a mutable clone of [method]
*
* Please note, this method allocates a [ClassProxy].
* Use [method] where possible.
*/
val mutableMethod by lazy {
mutableClass.methods.first {
MethodUtil.methodSignaturesMatch(it, this.method)
}
}
}

View File

@@ -12,7 +12,7 @@ import java.io.Closeable
* If it implements [Closeable], it will be closed after all patches have been executed.
* Closing will be done in reverse execution order.
*/
sealed interface Patch<out T : Context> : Closeable {
sealed interface Patch<out T : Context> {
/**
* The main function of the [Patch] which the patcher will call.
*
@@ -20,13 +20,6 @@ sealed interface Patch<out T : Context> : Closeable {
* @return The result of executing the patch.
*/
fun execute(context: @UnsafeVariance T): PatchResult
/**
* The closing function for this patch.
*
* This can be treated like popping the patch from the current patch stack.
*/
override fun close() {}
}
/**

View File

@@ -5,20 +5,23 @@ import app.revanced.patcher.patch.Patch
import kotlin.reflect.KClass
/**
* Annotation to mark a Class as a patch.
* Annotation to mark a class as a patch.
* @param include If false, the patch should be treated as optional by default.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Patch(val include: Boolean = true)
/**
* Annotation for dependencies of [Patch]es.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class DependsOn(
val dependencies: Array<KClass<out Patch<Context>>> = []
)
)
/**
* Annotation to mark [Patch]es which depend on integrations.
*/
@Target(AnnotationTarget.CLASS)
annotation class RequiresIntegrations // required because integrations are decoupled from patches

View 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)
}
}
}
}

View File

@@ -3,6 +3,7 @@
package app.revanced.patcher.util.patch
import app.revanced.patcher.data.Context
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.patch.Patch
import org.jf.dexlib2.DexFileFactory
@@ -19,7 +20,13 @@ sealed class PatchBundle(path: String) : File(path) {
internal fun loadPatches(classLoader: ClassLoader, classNames: Iterator<String>) = buildList {
classNames.forEach { className ->
val clazz = classLoader.loadClass(className)
if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) return@forEach
// Annotations can not Patch.
if (clazz.isAnnotation) return@forEach
clazz.findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class)
?: return@forEach
@Suppress("UNCHECKED_CAST") this.add(clazz as Class<out Patch<Context>>)
}
}.sortedBy { it.patchName }
@@ -41,18 +48,11 @@ sealed class PatchBundle(path: String) : File(path) {
arrayOf(this.toURI().toURL()),
Thread.currentThread().contextClassLoader // TODO: find out why this is required
),
StringIterator(
JarFile(this)
.entries()
.toList() // TODO: find a cleaner solution than that to filter non class files
.filter {
it.name.endsWith(".class") && !it.name.contains("$")
}
.iterator()
) {
it.realName.replace('/', '.').replace(".class", "")
}
)
JarFile(this)
.stream()
.filter { it.name.endsWith(".class") && !it.name.contains("$") }
.map { it.realName.replace('/', '.').replace(".class", "") }.iterator()
)
}
/**
@@ -68,8 +68,9 @@ sealed class PatchBundle(path: String) : File(path) {
* Patches will be loaded to the provided [dexClassLoader].
*/
fun loadPatches() = loadPatches(dexClassLoader,
StringIterator(DexFileFactory.loadDexFile(path, null).classes.iterator()) { classDef ->
DexFileFactory.loadDexFile(path, null).classes.asSequence().map { classDef ->
classDef.type.substring(1, classDef.length - 1).replace('/', '.')
})
}.iterator()
)
}
}

View File

@@ -1,10 +0,0 @@
package app.revanced.patcher.util.patch
internal class StringIterator<T, I : Iterator<T>>(
private val iterator: I,
private val _next: (T) -> String
) : Iterator<String> {
override fun hasNext() = iterator.hasNext()
override fun next() = _next(iterator.next())
}

View File

@@ -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
}

View File

@@ -0,0 +1,233 @@
package app.revanced.patcher.extensions
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.BuilderOffsetInstruction
import org.jf.dexlib2.builder.MutableMethodImplementation
import org.jf.dexlib2.builder.instruction.BuilderInstruction21s
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.immutable.ImmutableMethod
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
private object InstructionExtensionsTest {
private lateinit var testMethod: MutableMethod
private lateinit var testMethodImplementation: MutableMethodImplementation
@BeforeEach
fun createTestMethod() = ImmutableMethod(
"TestClass;",
"testMethod",
null,
"V",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(16).also { testMethodImplementation = it }.apply {
repeat(10) { i -> this.addInstruction(TestInstruction(i)) }
},
).let { testMethod = it.toMutable() }
@Test
fun addInstructionsToImplementationIndexed() = applyToImplementation {
addInstructions(5, getTestInstructions(5..6)).also {
assertRegisterIs(5, 5)
assertRegisterIs(6, 6)
assertRegisterIs(5, 7)
}
}
@Test
fun addInstructionsToImplementation() = applyToImplementation {
addInstructions(getTestInstructions(10..11)).also {
assertRegisterIs(10, 10)
assertRegisterIs(11, 11)
}
}
@Test
fun removeInstructionsFromImplementationIndexed() = applyToImplementation {
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
}
@Test
fun removeInstructionsFromImplementation() = applyToImplementation {
removeInstructions(0).also { assertRegisterIs(9, 9) }
removeInstructions(1).also { assertRegisterIs(1, 0) }
removeInstructions(2).also { assertRegisterIs(3, 0) }
}
@Test
fun replaceInstructionsInImplementationIndexed() = applyToImplementation {
replaceInstructions(5, getTestInstructions(0..1)).also {
assertRegisterIs(0, 5)
assertRegisterIs(1, 6)
assertRegisterIs(7, 7)
}
}
@Test
fun addInstructionToMethodIndexed() = applyToMethod {
addInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
}
@Test
fun addInstructionToMethod() = applyToMethod {
addInstruction(TestInstruction(0)).also { assertRegisterIs(0, 10) }
}
@Test
fun addSmaliInstructionToMethodIndexed() = applyToMethod {
addInstruction(5, getTestSmaliInstruction(0)).also { assertRegisterIs(0, 5) }
}
@Test
fun addSmaliInstructionToMethod() = applyToMethod {
addInstruction(getTestSmaliInstruction(0)).also { assertRegisterIs(0, 10) }
}
@Test
fun addInstructionsToMethodIndexed() = applyToMethod {
addInstructions(5, getTestInstructions(0..1)).also {
assertRegisterIs(0, 5)
assertRegisterIs(1, 6)
assertRegisterIs(5, 7)
}
}
@Test
fun addInstructionsToMethod() = applyToMethod {
addInstructions(getTestInstructions(0..1)).also {
assertRegisterIs(0, 10)
assertRegisterIs(1, 11)
assertRegisterIs(9, 9)
}
}
@Test
fun addSmaliInstructionsToMethodIndexed() = applyToMethod {
addInstructionsWithLabels(5, getTestSmaliInstructions(0..1)).also {
assertRegisterIs(0, 5)
assertRegisterIs(1, 6)
assertRegisterIs(5, 7)
}
}
@Test
fun addSmaliInstructionsToMethod() = applyToMethod {
addInstructions(getTestSmaliInstructions(0..1)).also {
assertRegisterIs(0, 10)
assertRegisterIs(1, 11)
assertRegisterIs(9, 9)
}
}
@Test
fun addSmaliInstructionsWithExternalLabelToMethodIndexed() = applyToMethod {
val label = ExternalLabel("testLabel", getInstruction(5))
addInstructionsWithLabels(
5,
getTestSmaliInstructions(0..1).plus("\n").plus("goto :${label.name}"),
label
).also {
assertRegisterIs(0, 5)
assertRegisterIs(1, 6)
assertRegisterIs(5, 8)
val gotoTarget = getInstruction<BuilderOffsetInstruction>(7)
.target.location.instruction as OneRegisterInstruction
assertEquals(5, gotoTarget.registerA)
}
}
@Test
fun removeInstructionFromMethodIndexed() = applyToMethod {
removeInstruction(5).also {
assertRegisterIs(4, 4)
assertRegisterIs(6, 5)
}
}
@Test
fun removeInstructionsFromMethodIndexed() = applyToMethod {
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
}
@Test
fun removeInstructionsFromMethod() = applyToMethod {
removeInstructions(0).also { assertRegisterIs(9, 9) }
removeInstructions(1).also { assertRegisterIs(1, 0) }
removeInstructions(2).also { assertRegisterIs(3, 0) }
}
@Test
fun replaceInstructionInMethodIndexed() = applyToMethod {
replaceInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
}
@Test
fun replaceInstructionsInMethodIndexed() = applyToMethod {
replaceInstructions(5, getTestInstructions(0..1)).also {
assertRegisterIs(0, 5)
assertRegisterIs(1, 6)
assertRegisterIs(7, 7)
}
}
@Test
fun replaceSmaliInstructionsInMethodIndexed() = applyToMethod {
replaceInstructions(5, getTestSmaliInstructions(0..1)).also {
assertRegisterIs(0, 5)
assertRegisterIs(1, 6)
assertRegisterIs(7, 7)
}
}
// region Helper methods
private fun applyToImplementation(block: MutableMethodImplementation.() -> Unit) {
testMethodImplementation.apply(block)
}
private fun applyToMethod(block: MutableMethod.() -> Unit) {
testMethod.apply(block)
}
private fun MutableMethodImplementation.assertRegisterIs(register: Int, atIndex: Int) = assertEquals(
register, getInstruction<OneRegisterInstruction>(atIndex).registerA
)
private fun MutableMethod.assertRegisterIs(register: Int, atIndex: Int) =
implementation!!.assertRegisterIs(register, atIndex)
private fun getTestInstructions(range: IntRange) = range.map { TestInstruction(it) }
private fun getTestSmaliInstruction(register: Int) = "const/16 v$register, 0"
private fun getTestSmaliInstructions(range: IntRange) = range.joinToString("\n") {
getTestSmaliInstruction(it)
}
// endregion
private class TestInstruction(register: Int) : BuilderInstruction21s(Opcode.CONST_16, register, 0)
}

View File

@@ -9,6 +9,5 @@ import app.revanced.patcher.annotation.Package
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class ExampleBytecodeCompatibility

View File

@@ -4,9 +4,9 @@ import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.or
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.*
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
@@ -119,7 +119,7 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
//
// Control flow instructions are not supported as of now.
method.addInstructions(
method.addInstructionsWithLabels(
startIndex + 2,
"""
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;

View File

@@ -9,6 +9,5 @@ import app.revanced.patcher.annotation.Package
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class ExampleResourceCompatibility

View File

@@ -1,8 +1,9 @@
package app.revanced.patcher.util.smali
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.extensions.label
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.newLabel
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@@ -35,7 +36,7 @@ internal class InlineSmaliCompilerTest {
method.addInstructions(arrayOfNulls<String>(insnAmount).also {
Arrays.fill(it, "const/4 v0, 0x0")
}.joinToString("\n"))
method.addInstructions(
method.addInstructionsWithLabels(
targetIndex,
"""
:test
@@ -44,7 +45,7 @@ internal class InlineSmaliCompilerTest {
"""
)
val insn = method.instruction(insnIndex) as BuilderInstruction21t
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
assertEquals(targetIndex, insn.target.location.index)
}
@@ -61,19 +62,19 @@ internal class InlineSmaliCompilerTest {
"""
)
assertEquals(labelIndex, method.label(labelIndex).location.index)
assertEquals(labelIndex, method.newLabel(labelIndex).location.index)
method.addInstructions(
method.addInstructionsWithLabels(
method.implementation!!.instructions.size,
"""
const/4 v0, 0x1
if-eqz v0, :test
return-void
""", listOf(
ExternalLabel("test",method.instruction(1))
)
""",
ExternalLabel("test", method.getInstruction(1))
)
val insn = method.instruction(insnIndex) as BuilderInstruction21t
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
assertTrue(insn.target.isPlaced, "Label was not placed")
assertEquals(labelIndex, insn.target.location.index)
}