Compare commits

...

437 Commits

Author SHA1 Message Date
Skylot f2ea6415c9 fix(cli): don't print stacktrace for incorrect options (#2140) 2024-04-20 18:06:30 +01:00
Skylot bc70f8eabb fix: use correct new line string for simple code writer 2024-04-20 17:37:45 +01:00
Skylot be25cbf8c2 fix: use common parser for manifest, verify app package 2024-04-20 17:37:45 +01:00
Skylot f9c0cad146 chore: update dependencies 2024-04-19 20:14:25 +01:00
Skylot b356ff76e1 fix: improve StringBuilder elimination (#2148) 2024-04-19 20:14:25 +01:00
Skylot ec9244a635 fix(gui): use common code for manifest parsing in debugger 2024-04-19 20:14:25 +01:00
omerfarukkykc a5bd64461d fix(gui): remember selected device in debugger (PR #2153)
* ADBDialog->launchApp() if multiple devices presented should let user select the one they desire.

* compare objects directly instead parsing

---------

Co-authored-by: Ömer Faruk KAYIKCI <omer.kayikci@tubitak.gov.tr>
Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
2024-04-19 19:02:12 +01:00
dependabot[bot] 54bf79ccc5 build(deps): bump gradle/wrapper-validation-action from 2 to 3 (#2149) 2024-04-15 18:17:48 +00:00
Skylot 6182332eef fix: avoid self-loop for exception handlers (#2147) 2024-04-11 23:07:45 +03:00
Skylot 37b57096ec fix: allow use FieldInfo as switch key (#2147) 2024-04-11 23:07:44 +03:00
Skylot 6aab8fabc9 chore: update dependencies 2024-04-11 23:07:41 +03:00
JustFor 665c1e57d2 fix(gui): update Messages_zh_CN.properties (PR #2146)
Sync new Jadx text.
2024-04-09 22:11:24 +01:00
Skylot 6e8affcbdc feat: add options to JadxArgs to change code new line and indent (#1945, #1948) 2024-04-08 21:51:24 +01:00
Skylot 41d6b0018e fix: add missing " * " on new line for block comments, flip addCodeComment args (#2145) 2024-04-08 21:34:34 +01:00
Skylot dbadbb01fc refactor: rename method collectArgsWithoutLoading into collectArgNodes in MethodNode (#2142) 2024-04-07 23:09:02 +01:00
Skylot 0f52077c5c feat: allow to set style for code comments (#2145) 2024-04-07 23:06:32 +01:00
Skylot ea861829c7 fix: support end block entry for mutli-entry loops (#889) 2024-04-06 22:49:32 +01:00
Skylot c1de235289 fix: in anonymous class checks ignore instance fields not used outside 2024-04-06 22:45:30 +01:00
Skylot 8f969d4e89 chore: update gradle and dependencies 2024-04-03 21:03:48 +01:00
Skylot 0c1f830f94 fix: lambda decoding and code generation (#2139) 2024-04-03 21:03:48 +01:00
Skylot 43c082e4da feat: replace Android resource ids with android.R fields (#2119) 2024-03-31 20:37:33 +01:00
Skylot ecdc4e6757 refactor: move constant collection into separate pass (#2119) 2024-03-30 21:51:02 +00:00
Skylot b865c9c687 refactor: allow store unresolved fields in ConstStorage (#2119) 2024-03-30 20:52:31 +00:00
xnumad 6b4976c593 fix(gui): handle paths where file name is null (#2136)(PR #2137)
* fix: Ignore invalid files

Avoid NullPointerException when using "Open files" or drag-n-drop

* refactor: Replace Stream API chain with loop

IntelliJ

* fix: Ignore invalid files

Avoid NullPointerException when using "Add files"

* fix: Fall back to complete path string

Instead of empty project name

* fix: Render tree

Project tree (sidebar) didn’t load
Toggling "View > Show flatten packages" threw a NPE here

* fix code formatting

---------

Co-authored-by: Skylot <skylot@gmail.com>
2024-03-29 22:30:01 +00:00
Skylot 2807dc5090 fix(script): add example script for resources rename (#2126) 2024-03-20 18:46:44 +00:00
Skylot 463d2b90fa fix: don't apply node positions and prevent eager loading for custom decompile modes (#2116) 2024-03-19 20:23:04 +00:00
Skylot bff00d101f fix(script): add option flags, fix missing script options in help 2024-03-19 20:22:50 +00:00
Skylot 1290ef63a2 fix(build): enable publish to maven for rename-mappings plugin 2024-03-16 21:58:03 +03:00
Skylot 49d2b34d84 chore: update dependencies 2024-03-16 21:58:00 +03:00
CKCat eecdfae73f fix(res): resolve some manifest decode errors (PR #2122)
* The elementSize may be larger than the actual size of the element chunk.

* end namespace chunk size can be any value.

* keep at least a warning.
2024-03-16 18:57:10 +00:00
JustFor 8760b4ddde fix(gui): copy strings without quotes (PR #2121)
* Update AbstractCodeArea.java

In general, we need data, not text in code. But now every time you copy the highlighted text, you copy the highlighted quotes as well. This often results in an extra need to delete the quotation marks around the sides, which is confusing.
Now when copying selected highlighted text, quotes are not copied in.

* Update AbstractCodeArea.java

fix code format

* additional checks, move to common method

---------

Co-authored-by: Skylot <skylot@gmail.com>
2024-03-16 18:55:57 +00:00
Andrei Kudryavtsev 3599b248a4 feat(gui): dragging tab appearance settings (#2120)(PR #2118) 2024-03-08 23:11:58 +03:00
bagipro 2fdd496518 fix(res): add indents for namespace declarations (PR #2114)
Co-authored-by: bagipro <bugi@bugi>
2024-03-01 16:32:47 +00:00
bagipro 278e3c2d47 fix(res): avoid duplicated XML attributes (PR #2112)
Co-authored-by: bagipro <bugi@bugi>
2024-02-27 18:49:09 +00:00
bagipro 881a716b8e fix(res): fixed XML proto parsing for removed debug data (PR #2111)
* Fixed XML proto parsing for removed debug data

* Fixed codestyle check

---------

Co-authored-by: bagipro <bugi@bugi>
2024-02-27 17:34:37 +00:00
Skylot a73c9e90fc fix(dex-input): improve error report message for invalid dex checksum 2024-02-26 19:36:28 +00:00
Skylot 56749b2afb chore: update dependencies 2024-02-25 22:38:00 +03:00
Andrei Kudryavtsev d7ec35791b feat(gui): tabs drag and drop reorder support (#1212) (PR #2109) 2024-02-25 19:36:46 +00:00
Skylot d51362ed50 fix: don't remove exception handlers (#2104) 2024-02-19 20:18:44 +00:00
Skylot 5c0c1daa71 fix(gui): use new RSTA line number formatter API to show source lines 2024-02-16 18:38:56 +00:00
Skylot 603ea3989a chore: update dependencies 2024-02-16 17:41:37 +00:00
Emiel Matthys 018ff98df7 feat(gui): remember save preference decision (PR #2103)
* First version

* Use dropdown

* Spotless

* Language strings and tests

* Comment out translated versions

* Remove more translations

---------

Co-authored-by: Emiel Matthys <emiel.matthys@guardsquare.com>
2024-02-15 18:00:37 +00:00
nitram84 5fbabdefca fix: NPE in unused EcxHandler block removal code (#2086) (PR #2104) 2024-02-15 17:57:30 +00:00
nitram84 13607fc8b6 fix(res): add missing namespace declarations (PR #2102)
* fix(res): add missing namespace declarations

* remove `writer.getIndent() == 0`

---------

Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2024-02-15 17:56:39 +00:00
DanielFi 0c33d723c8 fix: optimize switch fallthrough (PR #2054)
* cache post dom map between switch cases
* cache post dom map of whole methods
* calculate full post dom tree, fix switch out block detection

---------

Co-authored-by: Skylot <skylot@gmail.com>
2024-02-14 18:31:38 +00:00
Skylot 0143423dc9 fix: use empty line before field in correct place (#2101) 2024-02-12 15:39:41 +00:00
Skylot 21b1452485 chore: update gradle and dependencies 2024-02-12 15:39:40 +00:00
Skylot ecb8abb98e fix: correct rollback if type update failed (#2090) 2024-02-10 16:08:51 +00:00
Skylot a3a4fabd5a fix: store classes access flags in class set 2024-02-07 22:10:31 +03:00
Skylot edf6ce273c fix: clear node flags for custom decompilation mode 2024-02-06 21:54:41 +03:00
dependabot[bot] 1bb956a8b0 build(deps): bump gradle/wrapper-validation-action from 1 to 2 (PR #2099)
Bumps [gradle/wrapper-validation-action](https://github.com/gradle/wrapper-validation-action) from 1 to 2.
- [Release notes](https://github.com/gradle/wrapper-validation-action/releases)
- [Commits](https://github.com/gradle/wrapper-validation-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: gradle/wrapper-validation-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-05 18:38:02 +00:00
Away 276ee537e1 fix: remove unused ExcHandlers blocks (PR #2086)
* Removing unused ExcHandlers blocks

* Improving removing unused ExcHandlers blocks

* Removing gradlew of the commit

* Adding test 2 for UnreachableCatch

* Update jadx-core/src/test/java/jadx/tests/integration/trycatch/TestUnreachableCatch2.java

---------

Co-authored-by: Away-pp <vladimir@DESKTOP-KESF23K>
Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2024-02-04 18:02:34 +00:00
Andrei Kudryavtsev 7e628ad1a1 fix(gui): detecting tab click with mousePressed instead of mouseClicked (PR #2097) 2024-02-04 17:36:02 +00:00
Sebastian Gallese 0a40b53412 docs: update Build Status shield in README (PR #2095) 2024-02-01 17:20:02 +00:00
dependabot[bot] 682f90de2f build(deps): bump gradle/gradle-build-action from 2 to 3 (PR #2094)
* build(deps): bump gradle/gradle-build-action from 2 to 3

Bumps [gradle/gradle-build-action](https://github.com/gradle/gradle-build-action) from 2 to 3.
- [Release notes](https://github.com/gradle/gradle-build-action/releases)
- [Commits](https://github.com/gradle/gradle-build-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: gradle/gradle-build-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* migrate to gradle/actions/setup-gradle@v3

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2024-01-29 20:00:31 +00:00
Skylot 1a07ab8ab1 chore: update dependencies 2024-01-25 20:10:39 +00:00
Skylot d86449a8ea fix(res): ignore resource table entries with '../' in name 2024-01-25 20:09:21 +00:00
Skylot 75d2e540aa refactor: add new task executor, remove task barrier (#1879) 2024-01-25 19:45:23 +00:00
bagipro e73612b4d2 feat(res): disable XML pretty print (PR #2087)
Co-authored-by: bagipro <bugi@bugi>
2024-01-22 15:01:38 +00:00
Away a19aec9d9f fix: use correct order in removeUnreachableBlocks (PR #2085)
* Fixing wrong order in removeUnreachableBlocks
* Removing dead code in TestTernary4 test
* Do not clear instructions in detach blocks method
2024-01-21 16:05:02 +00:00
Skylot 61be1d8b0a fix(gui): allow to reset variable name, fix renames for inner classes 2024-01-18 20:36:51 +00:00
Skylot 4483533417 fix(gui): highlight word even if cursor at the end of that word (#2083) 2024-01-18 19:40:53 +00:00
Skylot fb703cd856 fix(gui): scroll to first error on jadx script check/run 2024-01-18 18:17:53 +00:00
Skylot c0ff7572ac fix(gui): round file size to block size for cache usage calculation 2024-01-17 18:25:35 +00:00
Skylot 8e7ffc8ddb fix: support switch over enum by ordinal 2024-01-16 20:40:51 +00:00
Skylot f994abee21 build: add Java 21 to Gitlab CI 2024-01-15 19:34:29 +00:00
Skylot 6f1dfb6848 chore: update dependencies 2024-01-15 22:18:45 +03:00
Skylot 306bc7abc3 feat(java-input): support jsr/ret opcodes (#2039) 2024-01-15 22:18:44 +03:00
Skylot 8ed48183c7 feat(plugins): add API for search/use other plugins 2024-01-15 22:18:44 +03:00
Skylot fdc3fe1a8d fix(java-input): use correct registers for dup2_x* opcodes 2024-01-15 22:18:44 +03:00
Away 2805770510 fix: removeUnreachable not searching in all blocks (PR #2082)
Co-authored-by: Away-pp <vladimir@DESKTOP-KESF23K>
2024-01-14 17:07:39 +00:00
Skylot 03052baabe chore: update dependencies 2024-01-11 19:10:44 +00:00
Iscle 23e643c686 fix(gui): attempt to resolve field rename issues with shortcut (#1440)(PR#2075)
This is an attempt to fix the issues that sometimes arise when renaming functions or variables using the "n" shortcut as stated in issue #1440.

The reasoning behind the change: The instance creation of the RenameDialog was somehow affecting the UI thread and not allowing for the key release event to be dispatched. By running everything inside the invokeLater block, this might get fixed as it will execute after all previous tasks are finished.
We now also only show the dialog after EVERYTHING is set up, not before.
2024-01-05 21:42:06 +03:00
Iscle faeae086d1 feat(gui): improve search bar behavior when using key bindings (PR #2074)
Before, the search bar would toggle, which was quite annoying. Now, it replicates IntelliJ's search bar behaviour.

1.1. If the user has selected text, use that as the search text
1.2. Otherwise, use the previous search text (or empty if none)
2. Select all text in the search bar and give it focus
2024-01-05 21:31:34 +03:00
Iscle 38e64fafbd feat: add an option to generate kotlin code for xposed (PR #2070)
* feat: add an option to generate kotlin code for xposed

A new setting was added, bumping the settings version to 19.

* refactor: applied spotless

* refactor: fixed formatting

* refactor: fixed formatting once again
2024-01-05 21:24:08 +03:00
Skylot e723c245ee fix: use correct type printer in json output mode (#2053) 2023-12-23 18:46:39 +00:00
Skylot 3e57dacfd3 chore: update dependencies 2023-12-21 19:00:33 +00:00
Iscle f5accc8464 feat: add support for xapk files (#1597)(PR #2064)
* feat: annotate JadxPlugin with NotNull

Allows for better Kotlin support

* feat: add support for custom resources loader

* feat: add support for xapk resources loading

* fix: rename "decode" to "load"

* refactor: annotate JadxCodeInput with NotNull

* feat: add support for xapk code loading

* feat: add xapk support to file filter

* fix code formatting

* revert NotNull annotation

* several improvements

* refactor: fix typo

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-12-21 18:46:40 +00:00
Skylot 238fe17df0 fix(gui): add hint for class renaming (#1590) 2023-12-18 18:14:56 +00:00
Skylot 295befbaaa fix: check class alias for Kotlin Intrinsics search (#1590) 2023-12-18 18:14:56 +00:00
dependabot[bot] 5f20033f18 build(deps): bump actions/upload-artifact from 3 to 4 (#2060)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 17:59:11 +00:00
dependabot[bot] 5f376c6e21 build(deps): bump github/codeql-action from 2 to 3 (#2061)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 17:57:37 +00:00
Skylot 68a99c5410 chore: update dependencies 2023-12-16 19:20:45 +03:00
Skylot 8db70ee7a5 fix(gui): resolve reset and commit issues for disk code cache 2023-12-16 19:20:43 +03:00
LanBaiCode b6155afd32 fix(gui): use correct type for generic params in Xposed snippet (PR #2057)
* Fix: Resolved an issue with incorrectly generated xposedMethodSnippet when the parameter type is generic.
Add: Introduced xposedGenerateFieldSnippet.

* fix code format

* Fixed: Resolved the issue where Xposed code generation was incorrect when dealing with generic parameters and alias fields.

---------

Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2023-12-11 17:14:10 +00:00
Away-pp d5bf9f20a6 fix: IndexOutOfBoundsException with negative register index (PR #2056)
Co-authored-by: Away-pp <awaytesting1@gmail.com>
2023-12-10 18:51:25 +00:00
Skylot 2d5c0fda4a fix: prefer early return for 'if-else-if' block (#2052) 2023-12-05 21:08:22 +00:00
Skylot 5d56001826 build(deps): migrate to Google fork of Smali 2023-12-04 20:42:09 +00:00
Skylot 2de91f9a3a chore: update gradle and dependencies 2023-12-04 23:18:51 +03:00
dependabot[bot] e06e6bd483 build(deps): bump actions/setup-java from 3 to 4 (#2051)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 19:55:32 +00:00
nitram84 1e57e85382 fix: gradle export for APKs without strings.xml (PR #2050) 2023-12-03 17:38:46 +00:00
LanBaiCode ca03406a3d fix: correct type for generic params in Xposed snippet, add fields support (PR #2047)
* Fix: Resolved an issue with incorrectly generated xposedMethodSnippet when the parameter type is generic.
Add: Introduced xposedGenerateFieldSnippet.

* fix code format

---------

Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2023-11-25 17:55:53 +00:00
Skylot 2d28da9b0e feat: allow to set zip max entries count using env var (#1751) 2023-11-16 20:48:08 +03:00
Skylot edb1717969 fix: support variables reuse for enum restore (#2042) 2023-11-16 20:48:07 +03:00
Skylot 1b51234edc chore: update dependencies 2023-11-16 20:48:07 +03:00
nitram84 e6d896d91e fix(plugin): check for valid identifiers from Kotlin metadata (PR #2041) 2023-11-10 22:29:54 +00:00
Skylot a989fa7e64 refactor(deobf): split deobfuscation conditions (#2040) 2023-11-05 20:22:15 +00:00
nitram84 f7002c7fad feat(deobf): implement a whitelist for deobfuscation (PR #2040) 2023-11-05 20:21:41 +00:00
Skylot 41d986bdca feat(script): use cache for compiled scripts 2023-10-27 23:35:03 +03:00
Skylot 192a8116f1 chore: update FlatLaf 2023-10-27 23:35:01 +03:00
Markslin 173d85bf84 fix(res): resolve resourceIds reuse issue (PR #2037)
Co-authored-by: mingyinsun <mingyinsun@tencent.com>
2023-10-24 18:47:17 +01:00
Skylot b310e92d7a fix: inline CMP instructions to help conditions merge (#2033) 2023-10-21 23:08:00 +01:00
Skylot 4a92ee07cc fix(gui): hide not needed popup action for not editable code 2023-10-21 23:08:00 +01:00
Skylot fdffe20c46 fix(gui): another try to fix code search 2023-10-21 23:08:00 +01:00
Skylot 15d464d620 fix(gui): improve code and method search (#2033) 2023-10-18 21:53:40 +01:00
Skylot 1bd4526e4c fix: improve restructure of nested try/catch/finally blocks (#2033) 2023-10-15 20:06:35 +01:00
Skylot 816308e3ca chore: update dependencies 2023-10-15 20:06:35 +01:00
rawer886 cfd851a980 fix: use correct imports for classes from default package (PR #2031)
* fix: use fully qualified package names for classes of defpackage

* fix: use correct imports for classes from default package (#2027)
---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-10-11 20:04:03 +01:00
Julian Burner d129be7e86 fix: update to Mapping-IO 0.5 (PR #2029)
* Update to Mapping-IO 0.5

* Fix Spotless error

* Fix Spotless error (2)

* Sort mappings by name when exporting
2023-10-09 18:46:51 +01:00
Skylot 6433fcef72 chore: update gradle and dependencies 2023-10-07 18:08:44 +01:00
Jan S d120556772 fix(res): add resource entry offset check (PR #2024) 2023-10-06 17:25:36 +00:00
Skylot 19f40b8549 feat(cli): add option to show available plugins list 2023-09-27 19:44:58 +01:00
Skylot 89acf73010 feat(plugins): cache available plugin list 2023-09-27 19:44:53 +01:00
Goooler b70276d896 fix(gui): update zh_CN translation (PR #2022) 2023-09-27 16:19:11 +01:00
5idereal 76b370e249 update zh_TW translation (#2021) 2023-09-26 17:15:09 +01:00
Skylot b73f2ef0b9 fix(res): place xml attributes on new line (#2012) 2023-09-24 20:40:33 +01:00
Skylot 762c2d70bd chore: fix typos 2023-09-24 18:53:39 +01:00
Skylot 4e81bdd76f chore: update dependencies 2023-09-22 20:32:40 +01:00
Skylot 4d87b447a6 chore: remove unused code in ClsSet class (#2020) 2023-09-22 20:31:56 +01:00
Roni Ragil Iman Khoirul 8f0d0e4f4d feat: add Indonesian translation (PR #2018)
* Add Indonesian translation

* fix load i18n resources with deprecated language codes

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-09-20 19:26:59 +01:00
Skylot c39a696977 feat(script): add methods for apply renames and reload tabs (#2008) 2023-09-15 21:50:47 +01:00
Cesaryuan b78a0257b4 fix(gui): improve highlight in UsageDialog (#2006)(PR #2007) 2023-09-15 16:19:13 +01:00
xnumad cdede7eca4 docs: update Arch Linux version badge (PR #2005) 2023-09-14 18:33:31 +01:00
Skylot 72180164d8 chore(script): add example for rename method param from annotation (#2004) 2023-09-12 20:22:14 +01:00
Skylot 2dc0db230c chore(script): adjust scripts format 2023-09-12 20:21:02 +01:00
Skylot 24657f6b3c fix(plugins): add a better way to init plugins options 2023-09-11 21:50:48 +01:00
Skylot e358476c71 fix: attempt to improve package rename checks (#1997) 2023-09-09 20:16:57 +01:00
Skylot 45a637f33b feat: allow to disable zip security checks (#1579, #980) 2023-09-09 16:10:59 +01:00
Skylot 76cf9596e2 build: fix win bundle script 2023-09-07 20:06:40 +01:00
Skylot 95c07a4e76 fix: synchronize attributes map writes (#2001) 2023-09-07 16:59:20 +01:00
Skylot 2d1d5a419c chore: update dependencies, resolve gradle warnings 2023-09-07 16:15:05 +01:00
dependabot[bot] f45547e2c1 build(deps): bump actions/checkout from 3 to 4 (PR #2000)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-04 18:57:40 +01:00
Jan S 1f463c7152 fix: exclude ZIP entries of less than 25MB uncompressed size from ZIP bomb check (#1570)(PR #1998) 2023-09-02 17:58:09 +01:00
Skylot 2c92c6b0a6 chore(script): add example deobf from toString script (#1996) 2023-09-01 21:18:58 +01:00
Skylot f83ee45ac8 chore: update gradle and dependencies 2023-08-31 21:31:56 +01:00
Jan S f695fafe74 chore(debugger): improve code and add details to error messages (PR #1982)
* chore: created list entry getter getRegListEntry for problematic code;
use specific containers/fields instead of SimpleEntry;
* chore: include details on the used ArtAdapter in error message
* add device info to registers exception

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-08-30 22:58:59 +01:00
Skylot f6ab105ef7 chore: use github issue forms for templates 2023-08-11 20:28:35 +01:00
Skylot f81a2c4ebb chore: update dependencies 2023-08-11 17:36:14 +01:00
Skylot c602b3d967 feat: add support for 'package-info' (#1967) 2023-08-07 18:46:49 +01:00
Skylot f213082da5 feat(gui): load available plugins from jadx-plugins-list 2023-08-05 22:41:51 +01:00
Skylot 8d26fa2a89 build: don't publish to maven rename-mappings plugin 2023-08-05 19:27:15 +01:00
Skylot a06231d007 fix(plugins): do not parse kotlin metadata if all options disabled 2023-08-05 19:26:28 +01:00
Mino 68b84ea786 feat(gui): allow user to set custom shortcuts (#1479)(PR #1980)
* feat(gui): allow user to customize shortcuts

* internal: fixed other constructor for jadx action

* make code area actions customizable

* show warning dialog when mouse button is commonly used

* applied code formatting

* code formatting and and moved string to resources

* moved action related classes to their own package

* added fix for actions with modifiers in macos

* ignore left click in shortcut edit

* applied code formatting

* warn user when a duplicate shortcut is entered

* save shortcut when key is pressed (instead of typed)

* fix node under mouse being ignored

Co-authored-by: skylot <118523+skylot@users.noreply.github.com>

* add missing import

* applied code formatting

* added custom shortcuts support to script content panel

* save shortcut when key is released (instead of pressed)

* enable custom shortcut in script autocomplete

* fix duplicate shortcut warning when the shortcut is set again at the same action

* fixed mouse buttons shortcut not working for code area

* fix exception with mouse button shortcuts

* fix action getting fired twice

* added variants for forward and back nav actions

* fix exception when shortcut is not saved

* fix mouse button shortcut for auto complete action

* consume mouse event if bound to an action

* workaround not being able to extend HashMap

* fix exception in script code area when using mouse button shortcut

* minor pref serialiazation improvement

* fix action buttons not working (like run action)

* fix exception with plugin actinos

* fixed nullptr when adding an action with null actionmodel to jadxmenu

* fix plugin action name not showing

---------

Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2023-08-03 20:05:25 +01:00
Skylot 868e99e0b5 build: add windows host for build tests 2023-08-01 18:44:53 +01:00
Mino 95db98e574 fix: resolve exception when androidmanifest.xml is not found (PR #1978) 2023-07-31 22:27:52 +01:00
Mino 63fc7e05b6 refactor: reuse the same parser in main activity action and gradle export feature (PR #1971)
* internal: reuse the same parser in Main Activity action and export gradle project

* removed unnecessary logs

* fixed code formatting issues

* removed no longer used methods

* optimize imports

* fix exception when app name isn't found

* use EnumSet instead of int for parse flags

* moved ApplicationParams class under android utils package

* moved attributes parsing to a seperate method

* fallback to any strings.xml if default one is not found
2023-07-31 18:50:47 +01:00
Jan S 2c2bb64c09 fix(tests): ResXmlGenTest fails on Windows (PR #1972)
* fix(core): fix ResXmlGenTest fails on Windows
* code style
2023-07-31 17:51:59 +01:00
Mino 0f5d07c6b1 feat(gui): hex-viewer for binary asset files (#198)(PR #1969)
* implemented hex-viewer feature

* added support for opening lib (.so) files

* removed unused class

* fix formatting

* fixed error when opening an empty file

* fixed a slight inaccuracy in synchronizing highlights

* defaulted little endian to false

* synchronize hex editor with theme and use smali font

* fixed wrong displayed values

* applied code formatting

* fixed selection color in preview panel

* Changed Smali Editor font entry in settings less confusing
2023-07-31 00:02:39 +01:00
Ohad Shai fbb6aa580e chore: upgrade to kotlin-logging 5 (PR #1966)
* upgrade to kotlin-logging 5
* apply code autoformatting
* resolve deprecation warnings

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-07-30 19:02:56 +01:00
Mino d9692a4f5f feat(gui): add 'go to main activity' action (#1423)(PR #1965)
* implemented feature: 'go to main activity'
* fix code formatting (added braces to if statements)
* search main activity by original class name, handle exceptions

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-07-29 21:03:25 +01:00
Mino ef0c09ca5b feat(gui): show number of search results in search bar (#1819)(PR #1964)
* gui: show number of search results in search bar, closes #1819
* optimized imports
* use string from resources
* applied code formatting
2023-07-28 22:17:04 +01:00
Skylot 752c61ae71 fix: disable zip file checks for new JVM versions (#1962) 2023-07-28 20:09:54 +01:00
Skylot 8f635076ea fix(gui): allow html for empty package name (#1961) 2023-07-26 21:03:55 +01:00
Skylot 8d5130a329 chore: update gradle and dependencies 2023-07-26 21:03:54 +01:00
Skylot 09a5e0893b fix: handle classes 'super' loop (#1942) 2023-07-24 21:59:27 +01:00
Jan S de8c315a7a fix(gui): display Quark stderr output in Jadx log (#1955)(PR #1957)
* Quark: also display error stream in Jadx log

* use info log level, open log viewer to track progress

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-07-24 18:03:37 +01:00
JustFor 3abdd40498 fix(gui): update Messages_zh_CN.properties (PR #1951) 2023-07-17 18:45:47 +01:00
Skylot 5b7ebec7e3 feat(gui): add option to set cache location, view/delete exists caches (#1941) 2023-07-17 00:49:40 +03:00
Yoav Sternberg de603ef909 fix(gui): support inline node for jump to code (#1905)(PR #1950) 2023-07-15 18:58:16 +01:00
JustFor a0a6779097 fix(gui): update Messages_zh_CN.properties (PR #1949)
1. Fill in the missing information
2. Sync new texts.
2023-07-12 16:36:43 +01:00
nitram84 ce9645864d fix(res): support formatted=false for strings (PR #1947) 2023-07-09 20:05:29 +01:00
Skylot d076c4e73d build: migrate to kotlin dsl in gradle scripts, update gradle and deps 2023-07-08 20:13:37 +01:00
Skylot 3e4c6a9c51 refactor(gui): don't use map for store open tabs order (#1940) 2023-07-06 17:14:31 +01:00
Skylot 74a2d7d5e7 doc: update contributing guide 2023-07-05 23:21:02 +01:00
bytemarx 219f9ebede feat(gui): close all tabs to the right (#1939)(PR #1940)
* feat(gui): close all tabs to the right (#1939)

* remove usage of LinkedHashMap as variable type

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-07-05 22:54:19 +01:00
Skylot ba0e918fef chore: update dependencies 2023-07-05 20:20:01 +01:00
Skylot 9b75a4f199 fix: update raung to fix stack frame issue (#1932) 2023-07-05 19:43:24 +01:00
Skylot 2d65594383 fix: additional checks for class alias string (#1937) 2023-07-04 21:25:38 +03:00
nitram84 36f439a82b fix(res): resolve all custom attributes (PR #1933) 2023-07-04 19:21:32 +01:00
5idereal 0efc99e43e fix(lang): update zh_TW translation (PR #1938) 2023-07-04 19:20:37 +01:00
nitram84 c0c35f32a8 fix(gradle): avoid BuildConfig conflict (#1847) (PR #1931) 2023-07-01 16:41:57 +01:00
Skylot 53e51c76a5 fix(plugins): use default args hash supplier in kotlin metadata plugin (#1929) 2023-06-30 16:33:22 +01:00
Midori Kochiya 14265a7b70 fix(res): parse strings only when they are referenced (#1926)(PR #1928)
* Parse strings only when they are referenced

* Fix style
2023-06-28 21:29:46 +01:00
nitram84 4467f9118f fix: update class set to Android 34, add apache http client support for api level 28+ (PR #1927)
* fix(cli): fix jcst converter

* extend jcst format, update class set to Android 34, add optional android libs

* fix(gradle): add apache http client support for api level 28+

* don't require existing core.jcst file for convert cls set, improve performance

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-06-28 20:54:28 +01:00
Julian Burner 240a903438 fix: don't override passed mappings path (#1891)(PR #1925)
* Don't override passed mappings path

* Fix spotless error
2023-06-28 18:20:34 +01:00
chaos afa331e8fc feat(gui): register macOS specific open file handler (#1922)(PR #1923)
* feature:支持双击文件后直接打开jadx-gui

* additional checks

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-06-20 18:36:37 +01:00
Skylot 6620e650ef fix: check args of inlined insns on reorder (#1919) 2023-06-19 15:05:06 +01:00
Skylot 2aa7438346 fix: check source file name alias uniqueness before rename (#1913) 2023-06-17 16:41:50 +01:00
Skylot 26fb7f33fb fix(script): use script log for show stages exceptions (#1912) 2023-06-15 21:12:01 +01:00
Skylot 6f4451364b fix(script): use compile instead analyze for scripts with deps (#1912) 2023-06-15 21:10:11 +01:00
Skylot 1a642108ef fix(script): temp workaround to disable script checks before run (#1912) 2023-06-14 09:43:51 +01:00
Skylot dddb7b2878 fix: don't inline consts into anonymous class constructor (#1901) 2023-06-13 21:35:15 +01:00
nitram84 82cafe6e94 fix(res): support min attr for attrs (PR #1909) 2023-06-12 14:50:10 +01:00
Skylot b578479270 fix(res): remove first empty line in public.xml (#1907) 2023-06-11 13:43:55 +01:00
Skylot 85b77cd3c0 chore: update dependencies 2023-06-11 13:42:47 +01:00
Skylot 38c3f8bf9a fix: additional insns reorder checks (#1904) 2023-06-10 23:36:14 +01:00
Yoav Sternberg f558203a9d feat(api): access node under caret/mouse and jump (PR #1903)
* Access node under caret/mouse and jump
* Apply lint
2023-06-09 15:50:57 +01:00
Skylot 347d6e2c2e refactor: improve github plugin resolver 2023-06-07 22:09:06 +01:00
Skylot eb4d540b04 chore: update dependencies 2023-06-07 21:20:05 +01:00
cskwrd 83b6ffbba9 fix(gui): fix spelling (PR #1897) 2023-06-07 16:47:39 +01:00
Skylot 50c5f0874f feat(gui): manage plugins in preferences window 2023-06-06 22:29:31 +01:00
Skylot a72e6aeafe feat(plugins): allow to set custom settings page in jadx-gui 2023-06-06 21:58:30 +01:00
Skylot 683cd76cc5 feat: add events support (#1832) 2023-06-06 21:58:29 +01:00
Goooler df313dbfec fix: replace jcenter with mavenCentral in export gradle files (PR #1896)
jcenter is deprecated, we can use mavenCentral as one of the default maven repo.
2023-06-06 13:26:01 +01:00
Skylot 2e3579a8fd fix(gui): disable rename action in tree popup if not allowed (#1890) 2023-06-03 14:38:33 +01:00
Skylot 99c8ceae9a fix(gui): don't reset package alias on class rename (#1732) 2023-06-03 16:20:44 +03:00
Julian Burner fa67f4fef7 fix: preserve renames on mapping export (#1732)(PR #1893) 2023-06-03 14:20:06 +01:00
Skylot e9a7ccd00c fix: don't change annotated code on finish (#1732)
Remove first empty line break annotation positions, can occur for classes in default package
2023-06-02 21:41:38 +01:00
Skylot 76e5824d99 feat(test): add util methods for renames in test (#1885) 2023-05-27 19:55:28 +01:00
Skylot 3bf101d7ac feat(gui): split settings into pages 2023-05-27 15:40:38 +01:00
myzhan c28320e1b2 feat(gui): add an action to prettify json file in resources (PR #1880)
* feat(gui): add an action to prettify json file in resources

* fix build

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-05-25 21:56:06 +01:00
nitram84 ea5b66d463 fix(gradle): add legacy support for vector drawables (PR #1879) 2023-05-25 20:19:26 +01:00
Skylot 8a67c39279 feat(cli): install and manage plugins from command line 2023-05-23 17:53:48 +01:00
Skylot 67054bda3d fix: add rename reason for method merged with bridge (#1876) 2023-05-20 14:06:31 +01:00
Skylot ed4c5a3a17 fix: use shadow jar in app bundle to reduce jars count (#1868) 2023-05-18 20:40:39 +01:00
Skylot ae1b1ce99e fix(build): use Kotlin with explicit project dependency 2023-05-17 21:26:55 +01:00
Skylot 5d3c96ead2 chore: update dependencies 2023-05-17 15:12:51 +01:00
Krzysztof Iwaniuk 63ad7fb128 feat: add option for hexadecimal integer format globally (PR #1869)
* option for hexadecimal integer format globally

* fix i18n strings, update readme

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-05-15 13:58:13 +01:00
Krzysztof Iwaniuk ccdbb1d62c feat: parse and use Kotlin metadata for renames (#1861)(PR #1860)
* initial setup for data class and metadata parsing
* bug fix & comments
* better version using plugin system
* added tests
* ignore getters test fix
* logs & imports
* reverted accidental changes
* moved util classes to plugin, spotless apply
* move test and some other minor fixes

---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-05-14 21:34:40 +01:00
Skylot 3474f0da04 fix: split loop exit at loop end (#1866)
Loop exit (like condition) in loop end block can confuse loop restructure.
To resolve this try to split back edge and make loop end a simple path.
2023-05-13 22:10:21 +01:00
Skylot 0fd9a9df29 fix: check for annotations before remove empty default constructor (#1863) 2023-05-11 18:11:02 +01:00
bagipro e29011eb95 fix(res): update Android resources to API 34 (PR #1864)
* update android resources to API 34
* fix format issues

---------

Co-authored-by: bagipro <bugi@bugi>
Co-authored-by: Skylot <skylot@gmail.com>
2023-05-11 17:26:02 +01:00
Skylot f52d2814df fix(gui): queue UI settings update (#1859) 2023-05-08 17:01:26 +01:00
Skylot 6e507f0cdc fix(script): don't use modules to build script classpath (#1858) 2023-05-07 20:19:38 +01:00
Skylot f825901849 chore: update dependencies 2023-05-07 20:17:46 +01:00
Skylot 5aaceee978 fix: cache finally extract checks on multiple paths (#1853) 2023-05-06 18:41:29 +01:00
Skylot f53dbbfebf feat: option to disable inner classes move to parent (#1817) 2023-05-06 20:39:50 +03:00
nitram84 d29263ef57 fix(res): fix escaping for string arrays 2023-05-06 20:38:13 +03:00
nitram84 d8c8fb4e18 test(res): add unit tests for ResXmlGen 2023-05-06 20:38:13 +03:00
Skylot 835f9fb5e0 chore(build): use official gradle build action 2023-05-01 21:31:36 +03:00
JustFor ecbcbbe8d4 fix(gui): update CN translation (PR #1852)
Fixed the problem of indistinguishable after English translation.
2023-04-28 22:25:15 +01:00
Skylot 37b1bff8f2 fix(gui): adjust node by offset search in code area (#1845) 2023-04-25 21:28:27 +01:00
Skylot 5e8f9b900f fix: remove class filtering on export (#1847) 2023-04-25 17:12:42 +01:00
Skylot ddefead764 chore(build): check java version (#1846) 2023-04-25 14:23:11 +01:00
Skylot 54f83cee1c fix: run code schrink after insn args simplify (#1838) 2023-04-24 22:25:09 +01:00
Skylot 7f3bd8c668 fix: additional checks for primitive deboxing (#1838) 2023-04-24 22:25:09 +01:00
Skylot ea10f2af47 fix(build): set jvm target version for all kotlin tasks 2023-04-24 22:21:20 +01:00
Skylot 2d810a2864 chore(build): update gradle 2023-04-24 22:21:20 +01:00
George Hopkins bac2386a1a fix: add parentheses around multiple lambda arguments (PR #1840)
* fix: add parentheses around multiple lambda arguments
* add simple test case
---------

Co-authored-by: Skylot <skylot@gmail.com>
2023-04-24 19:59:59 +01:00
Skylot 06ec12ba17 fix(script): use default script compiler args 2023-04-21 15:50:06 +01:00
JustFor 7f713e5592 fix(gui): update Messages_zh_CN.properties (PR #1831) 2023-04-21 14:42:02 +01:00
Skylot ee2556ecb6 fix(gui): update mappings node instead full reload on 'save as' (#1732) 2023-04-20 22:00:06 +01:00
Skylot e57787393e chore(build): fix gradle tasks dependencies 2023-04-20 19:46:59 +01:00
Skylot b7634024d6 chore: update gradle and dependencies 2023-04-20 17:22:16 +01:00
Skylot 9417671606 refactor: extract input-api into library (#1821) 2023-04-20 17:22:16 +01:00
Skylot 2e652b4219 fix(plugins): use loaded mapping tree on export (#1732) 2023-04-20 17:22:16 +01:00
Skylot 1d6ebed7e5 chore: fix spotless apply 2023-04-20 17:22:16 +01:00
Skylot 19cf22b9e3 chore: update Eclipse formatter config 2023-04-20 17:22:16 +01:00
Skylot 75fbdd3ac4 chore(build): set required Java version to 11, update dependencies 2023-04-20 17:22:16 +01:00
Skylot 13e317d927 fix(gui): resolve active tab restore issue 2023-04-20 17:22:15 +01:00
Skylot 7a309ca367 fix: improve plugins data handling 2023-04-20 17:22:15 +01:00
Skylot a992c93198 fix(plugins): improve custom passes merge ordering 2023-04-20 17:22:15 +01:00
Skylot ee3a653c1b fix(gui): show options from all plugins 2023-04-20 17:22:15 +01:00
Skylot e8e7028792 perf: improve usage data apply 2023-04-20 17:22:15 +01:00
Skylot 2403d32ae4 feat(gui): use plain json config instead java preferences 2023-04-20 17:22:15 +01:00
Skylot 1c89c7290c fix(gui): discard old settings migrations 2023-04-20 17:22:15 +01:00
Skylot 1891f6fd7e fix: ignore source name if current alias is better (#1795) 2023-04-20 17:22:14 +01:00
Skylot e933b41236 script: replace method call with calculated result (#1251) 2023-04-20 17:22:14 +01:00
Skylot 9f7432134c feat(gui): show loaded mapping file in input nodes 2023-04-20 17:22:14 +01:00
Skylot 7d69e5aaab feat(gui): add script log open button 2023-04-20 17:22:14 +01:00
Skylot 780b1a0d53 fix(gui): resolve ktlint deprecation warnings 2023-04-20 17:22:14 +01:00
Skylot ea37f3e9f7 fix: don't inline constants into lambdas 2023-04-20 17:22:14 +01:00
Skylot c6ed117df6 fix: improve internal state checks 2023-04-20 17:22:14 +01:00
Skylot cedcc29e01 chore(gui): add missing keys for new i18n file 2023-04-20 17:22:13 +01:00
Skylot d5219e7f0c feat: allow to move class to another package 2023-04-20 17:22:13 +01:00
Skylot 9a6dec0dbd fix: scripts runtime code and docs clean up 2023-04-20 17:22:13 +01:00
Skylot 17574ee554 chore: update dependencies 2023-04-20 17:22:13 +01:00
Skylot 926f4e497a feat(gui): allow to dock log viewer, new filter modes 2023-04-20 17:22:13 +01:00
Skylot acbe94df27 feat(script): add code area popup menu action 2023-04-20 17:22:13 +01:00
Skylot a2f018a00b feat(gui): add run, check and format script actions 2023-04-20 17:22:12 +01:00
Skylot 98cc1b1022 script: add sample deobf by code script (#1706) 2023-04-20 17:22:06 +01:00
Skylot 745f46f81f build: configure spotless for kotlin, apply code style fixes 2023-04-20 19:01:41 +03:00
Skylot 6912ed40b4 feat(gui): save usage data into disk cache 2023-04-20 19:01:41 +03:00
Skylot a89dbc1152 refactor: make input plugin api similar to pass plugins 2023-04-20 19:01:40 +03:00
Skylot 0c4d46ead5 refactor: move plugins-api module into jadx-core 2023-04-20 19:01:39 +03:00
Skylot eae9bac938 refactor: move mappings feature to separate plugin module 2023-04-20 19:01:39 +03:00
Julian Burner cb91c8c41c feat: mapping-io import support (#1531)(PR #1532)
* Add new CLI args for mapping files and deprecate args regarding jobf files (will be moved to the cache dir in the future)

* Add support for importing method arg mappings

Also change `mapping-file` to `mappings-path`, since folders are supported, too

* Add GUI for importing mappings

* Also show save file dialog when exporting mappings

* Fix crash on startup when `--mappings-path` parameter is set

* Include imported renames when exporting mappings

* Add "close mappings" menu entry

* Don't instantiate MappingTree unless actually needed

* Terminology: `import` → `open`; `export` → `save`

* Save location of open mapping file into project data

* Correctly reset cache when loading new mappings

* Remove unused import

* Save opened mappings' last modified date to reset cache when changed

* Fix if statement

* Correctly handle absence of mappings path in project data

* Show overwrite warning for folders only if not empty

* Prevent crash when imported mappings don't have any namespaces

* Handle wrong mappings namespace count error

* Replace unneeded public with private

* Add option for saving open mappings directly to disk

* Correctly propagate and throw exceptions during decompiler init

* Respect opened mappings' existing namespaces; fix related crash

* Deduplicate code, add `DalvikToJavaBytecodeUtils` class

* Small cleanup; move more functionality to utility class

* Support for importing class, field and method mappings

* Handle mappings in RenameDialog

* Fix checkstyle

* Fix wrong naming order

* Use modified mapping-io JAR from https://github.com/skylot/jadx/commit/18070eb7a649db0b0daef38d456316d5b4650072

That commit got rid of redundant embedded libraries

* Add null checks

* Check if mapping tree is null before running MappingsVisitor

* Use working mapping-io build

* Handle cache invalidation directly in DiskCodeCache class

* Don't reset UserRenamesMappingsMode if project is just reloaded

* Fix checkstyle

Co-authored-by: Skylot <skylot@gmail.com>
2023-04-20 19:01:38 +03:00
Skylot cb1f3e9843 refactor: use package nodes in api and ui 2023-04-20 19:01:38 +03:00
Skylot d4927db52b refactor: split and simplify deobfuscator 2023-04-20 19:01:37 +03:00
Skylot bc7300bd01 feat: add package node, allow to rename packages 2023-04-20 19:01:36 +03:00
Skylot 278d7fa3f9 feat(script): add options support 2023-04-20 19:01:35 +03:00
Skylot d9af91bc4d feat(gui): add auto complete for jadx scripts 2023-04-20 19:01:35 +03:00
Skylot 18fe9f305c feat(gui): support scripts in UI 2023-04-20 19:01:34 +03:00
Skylot e5e64365fc feat: add base scripting support 2023-04-20 19:01:29 +03:00
Skylot fdf170529f fix: use strict patterns for synthetic methods inline (#1829) 2023-04-19 17:52:37 +01:00
Skylot 50283ab543 fix: additional checks to forbid inline of null consts (#1828) 2023-04-19 15:49:06 +01:00
Skylot 3fa3e5acec fix: correct args shift for instance invoke-custom (#1816) 2023-04-16 20:10:57 +01:00
Skylot 4230cd5b5a feat(plugins): allow to load classes using input stream or byte array in jadx-input plugin (#1457) 2023-04-10 21:28:53 +01:00
nitram84 1ad6527de5 fix(xml): use parent attibute only for styles (PR #1815) 2023-04-10 18:34:17 +01:00
nitram84 0421ad80c1 fix: filter invalid chars in app name for gradle export (PR #1813) 2023-04-08 18:16:14 +01:00
nitram84 35e0201f06 fix(gradle): fix gradle build with version 7.4.2 - 7.6 2023-04-08 19:49:15 +03:00
nitram84 118eea5e77 fix(res): set empty parent for styles without a parent, remove duplicated code 2023-04-08 19:49:15 +03:00
nitram84 7f317be325 fix(res): resolve declare-styleable atrributes 2023-04-08 19:49:15 +03:00
nitram84 e1aa9f6de4 fix(res): resolve custom attributes 2023-04-08 19:49:15 +03:00
nitram84 058a5e3bb2 fix(res): resolve int hex attributes 2023-04-08 19:49:15 +03:00
JustFor 92b49ec2b5 fix(gui): update Messages_zh_CN.properties (PR #1811)
sync new text, and Some symbols are translated
2023-04-07 19:20:15 +01:00
Skylot 583a04b092 fix(gui): show skipped resources count during search (#1808) 2023-03-24 22:00:07 +00:00
Skylot 444a04e2f7 fix(gui): redirect jump from search for inlined classes 2023-03-24 15:34:46 +00:00
Skylot 157e702ffd feat: inline lambdas by instance field (#1800) 2023-03-24 15:34:45 +00:00
Jan S 77892f41ec fix(res): parsing of sparse RES_TABLE_TYPE_TYPE and RES_TABLE_TYPE_STAGED_ALIAS chunks (#1806 #1803)(PR #1807) 2023-03-23 17:30:29 +00:00
Jan S 6ba0e1dbf6 fix(res): handle RES_TABLE_TYPE_OVERLAY (#1748) (PR #1804) 2023-03-18 14:23:49 +00:00
Skylot 950fbbaa83 fix: restore missing type parameter declarations (#1800) 2023-03-17 20:28:42 +00:00
Skylot 912c431511 fix(debugger): process UI updates in correct thread (#1796) 2023-03-17 14:23:36 +00:00
Skylot 5d6b82724a fix(gui): search constant fields usage in all classes (#1801) 2023-03-16 17:41:23 +00:00
Jan S 78c976ad4f fix(res): resolve manifest decoding error Expected strings start (#1797)(PR #1798) 2023-03-10 18:46:31 +03:00
Skylot fbdfd135da fix(cli): use common enum args parser (#1787) 2023-02-27 19:35:49 +00:00
Jacob Davis-Hansson dd51783d9e fix(cli): make enum CLI arguments match documented format (PR #1787)
Currently if you do `jadx --help`, it says the `--deobf-cfg-file-mode` option accepts the value `read-or-save`. 

However, if you give it that option, it instead prints the following error message:

```
java.lang.IllegalArgumentException: 'read-or-save' is unknown, possible values are: read, read-or-save, overwrite, ignore
	at jadx.cli.JadxCLIArgs$DeobfuscationMapFileModeConverter.convert(JadxCLIArgs.java:524)
	at jadx.cli.JadxCLIArgs$DeobfuscationMapFileModeConverter.convert(JadxCLIArgs.java:516)
	at com.beust.jcommander.JCommander.convertValue(JCommander.java:1340)
	at com.beust.jcommander.ParameterDescription.addValue(ParameterDescription.java:249)
	at com.beust.jcommander.JCommander.processFixedArity(JCommander.java:920)
	at com.beust.jcommander.JCommander.processFixedArity(JCommander.java:901)
	at com.beust.jcommander.JCommander.parseValues(JCommander.java:731)
	at com.beust.jcommander.JCommander.parse(JCommander.java:363)
	at com.beust.jcommander.JCommander.parse(JCommander.java:342)
	at jadx.cli.JCommanderWrapper.parse(JCommanderWrapper.java:37)
	at jadx.cli.JadxCLIArgs.processArgs(JadxCLIArgs.java:211)
	at jadx.cli.JadxCLI.execute(JadxCLI.java:35)
	at jadx.cli.JadxCLI.main(JadxCLI.java:20)
```

This commit changes all the enum parsers to do the inverse string of `enumValuesString`, so the documented behavior works.
2023-02-27 19:08:07 +00:00
Skylot 158fc2fca3 chore: update raung version 2023-02-18 15:46:08 +00:00
Skylot 24284a6f3a fix: process manifest before other resources (#1740) 2023-02-17 17:54:07 +00:00
Skylot 85c2c63aa3 fix: output unknown invoke-custom as polymorphic call (#1760) 2023-02-11 16:06:30 +00:00
Skylot f354f7de63 fix(gui): split tabs loading to prevent ui dead lock 2023-02-11 14:01:31 +00:00
Skylot 540c0a8100 feat: support polymorphic invoke (#384)(#1777) 2023-02-03 16:28:24 +00:00
Skylot 4d00fede56 fix: resolve JavaNode caching issues (#1775) 2023-02-02 19:39:42 +00:00
Skylot b1bc5c08ff chore: update dependencies 2023-02-02 15:23:54 +00:00
Ran Naor 305d4f4fe5 fix(gui): print the renamed function name in a frida snippet log (#1772)(PR #1773)
* frida snippet log now prints the correct method name if the method was renamed
* fixed spotless check in frida snippet
* get the renamed method name from the alias proprety and changed to format string to fit frida-trace style
* fixed import order to fix gradle spotless warning
2023-01-28 17:59:42 +00:00
Ran Naor 2d149e9a5d fix(gui): allow html in JVariable to render renaming of variables correctly (#1769)(PR #1770) 2023-01-27 17:03:43 +00:00
Ran Naor 87b9ff3c35 feat(gui): added keyboard shortcut ctrl+w to close tab (#1765)(PR #1766) 2023-01-21 17:22:36 +00:00
Zach Snell 1c36b3c74c fix(gui): quick fix for duplicate/overlapping logcat windows (#1752)(PR #1761)
* Simple fix to duplicate/overlapping logcat windows. Could be improved by not re-creating UI every time
* Apply suggestions from code review
* Another try to fix trailing spaces using GitHub suggestions

Co-authored-by: Zach Snell <zach.snell@bivalogic.com>
Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2023-01-21 17:07:27 +00:00
Skylot 068e4b8e3d fix: allow altMetafactory method in lambda call site (#1760) 2023-01-15 16:08:34 +00:00
Skylot df38a6424f fix(gui): make bytecode output closer to smali (#1739) 2022-12-25 18:53:25 +00:00
Skylot 5d186e56a5 chore: update dependencies 2022-12-25 18:53:25 +00:00
Skylot 0fafcfa006 fix(gui): improve smali disasm method param write (#1739) 2022-12-13 17:40:00 +00:00
jmlitfi e3fdbafd86 fix(gui): resolve exception in smali method writer (#1739)(PR #1745)
Co-authored-by: jmlitfi <jeffmlitfi@gmail.com>
2022-12-13 17:39:20 +00:00
bagipro 07c2b14479 fix: escape special characters in AAB resources (PR #1747)
Co-authored-by: bagipro <bugi@macbook-pro-3.local>
2022-12-13 17:34:16 +00:00
Artem Zhiganov cdc844aaf3 feat(gui): add Russian Translation (PR #1744) 2022-12-10 13:11:34 +00:00
Skylot e1b7d361b9 fix: check full signature for search method override (#1743) 2022-12-09 17:13:01 +00:00
Skylot 12ef29bebc chore: update gradle and dependencies 2022-12-09 17:13:01 +00:00
Skylot 22ed241d50 fix(gui): correct html render in comments search results 2022-11-15 13:53:48 +00:00
Shatyuka 28e5a3c5be fix(gui): hi-dpi main window initial size (#1728)(PR #1729) 2022-11-14 18:19:09 +00:00
Skylot bb4d88cc68 fix(gui): add template for constructor and void methods to Frida snippet (#1714) 2022-11-07 19:13:11 +00:00
Mathis Hesse 4aaea2b93f fix(gui): change callMethodName of constructors in Frida action (#1714)(PR #1715)
* Change callMethodName of constructors in Frida action

* Fix format violation in FridaAction

* Fix format violation in FridaAction
2022-11-07 19:11:22 +00:00
Skylot bc8d7c4fc3 feat(gui): add native libs info to summary (#1717) 2022-11-03 19:15:33 +00:00
Skylot 5ea6c46778 fix(gui): show all code sources in summary (remove dex filter) (#1716) 2022-11-03 18:25:22 +00:00
Skylot b28f8ba85b fix(gui): try to handle exception in RSTA.getPreferredSize() (#1712) 2022-10-29 21:17:17 +01:00
Skylot 4db50fb749 fix(gui): correct html disabling in search results 2022-10-27 10:29:50 +01:00
Skylot 1dd0c90a04 build: switch java version to fix jdk install issue 2022-10-26 20:34:22 +01:00
Skylot 2bace2bde2 fix(gui): disable shell folders in file open dialog (#1709) 2022-10-26 20:08:57 +01:00
Skylot 1a9cb832ab feat(gui): add alternative file open dialog (#1709) 2022-10-26 19:58:58 +01:00
Skylot 6844a46c93 fix: disable HTML rendering in labels if not needed 2022-10-20 15:58:23 +01:00
Skylot e9e45707da chore: update dependencies 2022-10-20 14:54:31 +01:00
Skylot b9d02ff4c4 refactor: remove all LinkedList usage 2022-10-12 17:05:08 +01:00
Jan S 29b64300bc fix(gui): multi-threading issue in DebugController fixed (#1701) (PR #1702) 2022-10-11 19:21:06 +01:00
zhongqingsong 777355e86e fix(gui): update Messages_zh_CN.properties (PR #1700)
Add new text about logcat.
2022-10-10 18:57:58 +01:00
Skylot 620a177ce8 fix: restore enum class with custom code in static init (#1699) 2022-10-08 21:54:06 +01:00
Skylot 683c2dfbeb fix: improve ternary inline, resolve more enum cases (#1686) 2022-10-07 15:51:11 +01:00
Skylot 266cbcc6f4 fix(gui): migrate to fixed jdwp library fork (#1471) 2022-10-06 19:47:15 +01:00
Jan S 8a45602ae6 fix: improve logging messages for zip security errors (#750)(PR #1698)
Logging error messages on invalid file-names or path traversal attacks improved
2022-10-06 19:31:42 +01:00
Skylot 711419a797 fix: correct fix for all use places of incompatible primitives (#1688) 2022-10-03 00:11:04 +03:00
Skylot 603f3057eb chore: update dependencies 2022-10-03 00:11:01 +03:00
5idereal fa6fc1f871 fix(gui): update zh-TW translations (PR #1694) 2022-10-02 15:50:11 +01:00
Skylot 49fa320989 fix: handle possible concurrent exception in method codegen (#1685) 2022-09-29 20:28:01 +01:00
Skylot 2f301bf150 fix: don't mark constructor for inline if anonymous class inline is disabled (#1680) 2022-09-25 17:47:53 +01:00
Skylot b4892ce17f build: use fixed java version to build win artifacts 2022-09-23 21:08:16 +01:00
Skylot 151c171616 fix: handle empty block at end of else-if chain (#1674) 2022-09-23 20:40:56 +01:00
Skylot 79477a2de3 fix: don't rename bridged overridden methods (#1672) 2022-09-23 19:16:34 +01:00
dependabot[bot] 78aadda931 build(deps): bump actions/checkout from 2 to 3 (PR #1669)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-12 18:30:35 +01:00
Jan S b50706505f fix(res): implemented parsing RES_TABLE_TYPE_LIBRARY chunks (#1663)(PR #1664)
* core: Implemented parsing RES_TABLE_TYPE_LIBRARY chunks

* skip unknown data at the end of type chunk
2022-09-10 16:58:26 +01:00
The Cobra Chicken 9114821fb1 feat(debugger): add logcat output (#1411)(PR #1666)
* Adding logcatController class and writing adb / debugger panel information to the controller.

* Finished parsing logcat binary output and writing an arraylist containing all events.

* added highlighting of logcat output based on type.  Added timestamp parsing.

* Updated code to only get new log messages.

* Added additional code for select all

* Completed Check and uncheckall options.

* Changed log highlighting to log color.  Changed from JTextArea to JTextPane. Logcat pane will now autoscroll only if it is already scrolled to the bottom.  Debugger exit will now stop logcat as well.

* Moved labels into NLS rather than using hardcoded strings.

* Implemented the ability to autoselect attached process.  Changed the formatting of logcat messages.

* Moved labels into NLS rather than using hardcoded strings.

* updating to use info getter methods rather than directly accessing variable

* Added Logcat Pause Button

* Added Clear button

* Updated clear icon

* Cleaning warnings

* cleaning

* Changed behavior to only show logcat for debugged process to start with.

* cleaning

* cleaning

* cleaning

* applying spotless

* Fixing bug with switch

* fixed formatting issue

* add missing localization strings

Co-authored-by: green9317 <38409554+green9317@users.noreply.github.com>
Co-authored-by: TheCobraChicken <jeffmlitfi@gmail.com>
Co-authored-by: Skylot <skylot@gmail.com>
2022-09-08 15:18:55 +01:00
Skylot 1195582da8 feat(gui): option for search results count per page (#1652) 2022-09-05 20:07:02 +01:00
Skylot 258987b0ff chore: update dependencies 2022-09-05 20:07:01 +01:00
Guilherme a6a734c70d fix(gui): update pt-BR translation (PR #1655) 2022-09-01 15:51:12 +01:00
Choiman1559 d6c23a2a9b fix(gui): update Korean translation (PR #1650)
* update korean
* Update Messages_ko_KR.properties
* Restore missing empty line

Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2022-08-20 17:59:37 +01:00
Skylot db028904d7 fix(gui): set legacy sort flag also for launch4j (#1628) 2022-08-20 17:37:26 +01:00
Skylot 63a571306c refactor: load resource table nodes in one change (#1648) 2022-08-19 22:15:04 +01:00
Skylot bc4db61e25 fix(gui): improve resources search (#1648) 2022-08-19 15:52:14 +01:00
Jan S c7e6e28830 fix(gui): improve log viewer dialog (#1311)(PR #1649)
* [gui]: improve log viewer dialog

* use method from UiUtils to set window icons

Co-authored-by: Skylot <skylot@gmail.com>
2022-08-18 19:55:41 +01:00
Skylot 1d7b6fdb2c fix(gui): additional checks on open search result (#1647) 2022-08-18 15:59:45 +01:00
Skylot ce5d8eeff8 fix: don't inline anonymous in self inner class (#1645) 2022-08-18 15:48:17 +01:00
Jan S 894e0e6132 fix: UnsupportedOperationException on adding a field (#1645)(PR #1646)
* fix: UnsupportedOperationException on adding a field

* changed list check and creation similar to safeAdd
2022-08-18 15:33:18 +01:00
Skylot 127f0ecf3f fix(gui): disable actions if files not loaded (#1644) 2022-08-16 21:28:57 +01:00
Skylot cf7767e702 fix(gui): handle null value in TableCellRenderer (#1642) 2022-08-16 20:48:23 +01:00
Skylot e0aedc7949 fix: improve top block search for try/catch (#1633) 2022-08-15 21:31:26 +01:00
Skylot bad78de74c perf: improve directory delete 2022-08-14 13:38:12 +01:00
Skylot 12df8a169f chore: update gradle and dependencies 2022-08-13 18:25:08 +01:00
Skylot 15c9d33339 fix(gui): handle possible classes overlap in disk cache (#1633) 2022-08-13 13:13:13 +00:00
Jiaxin Peng 7e0fafbaf1 fix(gui): fix broken FileDialog by using legacy sort (#1628)(PR #1630)
#1628 #1606 #1213 #1574 #1552
2022-08-11 13:59:46 +01:00
zhongqingsong 57b9c1dd7a fix(gui): update Messages_zh_CN.properties (PR #1627) 2022-08-11 13:29:15 +01:00
Skylot 8ba0c17259 fix: handle empty endless loop (#1611) 2022-08-10 22:07:52 +01:00
Areizen cd32151083 fix(gui): correct Frida snippet for constructor (PR #1605)
When hooking a constructor with Frida, call `$new` instead of `$init`. `$init` cannot be used to instantiate an object and is reserved for hooking.

Co-authored-by: Your Name <you@example.com>
2022-08-06 20:16:37 +01:00
Skylot 75b52d672e feat(gui): save project search history 2022-08-05 20:43:05 +01:00
Skylot 11d04508f7 feat(gui): add manual search, stop and sort actions to search dialog (#1600) 2022-08-05 20:09:33 +01:00
Skylot e641b773b5 fix(gui): improve search dialog performance 2022-08-05 14:53:48 +01:00
Skylot 6e5899c654 fix: checks for field init reorder (#1599) 2022-08-04 17:38:46 +01:00
Skylot c66ffaa7f9 feat(gui): show start page on jadx open 2022-08-03 16:44:55 +01:00
Skylot 5193c6a5d8 chore: add tool for automatically insert new i18n lines 2022-08-03 16:44:40 +01:00
Skylot e7212af547 chore: upgrade gradle wrapper to 7.5 2022-08-03 18:44:10 +03:00
FixTheBug 3ca1357af4 fix(gui): sort resources by deobfuscated name (#1595)(PR #1598)
Co-authored-by: /paul-nguyen-goldenowl <paul.nguyen.goldenowl@gmail.com>
2022-08-01 14:54:22 +01:00
Guilherme 90e95213e4 feat(gui): add Brazilian Portuguese translation (PR #1596)
* feat(translation): add pt-br
* fix: `adb_dialog` prefix deleted
2022-08-01 12:15:35 +01:00
Jan S ae2d4da585 fix(res): XML "@null" decoding (#1583)(PR #1594)
minor improvements
2022-07-31 13:50:32 +01:00
Skylot 691d5cd1e6 fix: hide unused label before exception handler in simple mode 2022-07-30 17:33:23 +01:00
Skylot 58a46c6417 fix(gui): add constructors usage into class usage (#1591) 2022-07-30 17:22:32 +01:00
Skylot d3f6160e62 feat: add option to disable finally block extraction (#1592) 2022-07-30 14:07:43 +01:00
Skylot 03e4afb12f fix: check variables before merge in finally block (#1592) 2022-07-30 13:48:53 +01:00
Hen_Ry 6802f6028e fix(gui): update german translation (PR #1554)
* german update
* Fix
* applied the latest changes as discussed

Co-authored-by: Jan Peter Stotz <jpstotz@users.noreply.github.com>
2022-07-27 17:45:36 +01:00
SiderealArt 5ca61cfe18 fix(gui): update zh-tw translation (PR #1589) 2022-07-27 11:16:11 +01:00
Julian Burner 32d55b48f2 fix(gui): replace mixed-up quotation marks with period (PR #1588) 2022-07-26 10:16:34 +01:00
Skylot ab4b6f9e54 feat: select better resource name (#1581) 2022-07-25 19:53:03 +01:00
Jan S 9100ad1220 fix(debugger): resolve NPE in adb device viewer (#1585) (PR #1586) 2022-07-25 17:44:55 +01:00
Skylot 8b4f8fb572 fix: resolve inherited method to use correct alias (#1582) 2022-07-24 19:15:52 +03:00
Skylot 87e0e5bf16 fix: correct inline/merge with overriden bridge method (#1580) 2022-07-20 14:49:37 +01:00
Skylot e4c2d6cf6e fix(gui): use editor font for usage label 2022-07-20 14:36:51 +01:00
Skylot fb0bdb5112 fix(gui): allow to use empty name to reset rename 2022-07-20 14:35:53 +01:00
Skylot f4b3645435 fix: ignore anonymous classes in enclosing node search (#1580) 2022-07-19 19:25:17 +01:00
Skylot c27f2badf7 fix(gui): resolve payload offset for switch insns in debug smali code (#1575) 2022-07-18 18:50:48 +01:00
Skylot 1a877d6535 fix: resolve possible decompilation double execution 2022-07-16 22:29:59 +03:00
Skylot 5ada9331b6 chore: update dependencies 2022-07-14 14:33:04 +01:00
Skylot a0f4ccb7a4 fix: update deps and fix proto resource loading (AAB) (#1129) 2022-07-14 14:33:04 +01:00
Skylot 5b5524a7dd fix: handle parent of inlined/moved classes (#1578) 2022-07-14 14:40:13 +03:00
Jan S 3cc464c9c9 fix: IndexOutOfBoundsException in JumpManager (#1576) (PR #1577) 2022-07-13 17:24:20 +01:00
Skylot 51555667cf fix: add more checks before remove or rename enum methods (#1572) 2022-07-07 16:55:32 +01:00
Skylot e01ea7010f fix: save classes with code generation error into cache (#1568) 2022-07-03 19:32:41 +01:00
Skylot 77732c83c9 fix(gui): ignore/limit waiting of canceled search task (#1568) 2022-07-01 17:57:59 +01:00
Skylot a67fc83949 fix: better dominators algorithms 2022-07-01 17:26:54 +01:00
Skylot 3d920725aa fix: check synthetic methods before remove/inline (#1560) 2022-06-29 19:19:54 +01:00
Skylot 2f2fbea558 fix(gui): check user renames (#1557) 2022-06-29 16:21:49 +01:00
Skylot e7a86a2960 fix(gui): forbid rename method args in fallback mode (#1558) 2022-06-29 15:25:23 +01:00
Skylot b282d97ffe fix(gui): set current dir directly in file chooser constructor (#1553) 2022-06-28 16:57:57 +01:00
Skylot e4ca52a95f chore: update dependencies 2022-06-28 16:23:31 +01:00
Skylot d972d9ec74 fix(gui): ignore errors on code area dispose (#1545) 2022-06-28 16:20:31 +01:00
Jan S 0721a6b050 fix(gui): QuarkReport data validation added and other minor improvements (PR #1556)
* QuarkReport: data validation added and other minor improvements
* checkStyle
2022-06-25 22:24:53 +03:00
zhongqingsong 762ee6550e fix(gui):complete Chinese translation (PR #1549)
1、Complete Chinese translation
2、Previous word polish
2022-06-25 22:15:22 +03:00
Skylot 18070eb7a6 fix(gui): allow to select file on mapping export 2022-06-20 14:19:59 +01:00
Skylot 8486891728 fix(gui): resolve various minor issues 2022-06-20 13:17:50 +01:00
Skylot 4679172d4f fix(gui): correct set highlighted text in search (#1507) 2022-06-20 13:16:42 +01:00
Skylot 92a6c333d8 fix(gui): force jadx new version check by default 2022-06-20 12:55:15 +01:00
Skylot 358adbdd65 feat(gui): allow to disable jump on double click (#1540) 2022-06-19 17:19:08 +01:00
Skylot 65f7c80222 feat(gui): add reload and live reload actions (#1537) 2022-06-18 20:20:11 +01:00
Skylot d2e6bb236e fix: use wide move for long/double store/load java opcodes 2022-06-16 16:26:14 +01:00
Skylot eaeb114258 fix: check class name collisions (#1526) 2022-06-15 18:43:33 +01:00
Skylot 1533b7fe6e fix: keep types on duplicate cast remove (#1527) 2022-06-12 21:55:12 +01:00
Julian Burner a2cd8e1ead feat(gui): support export to deobfuscation mapping file formats (#1491)(PR #1505)
* Add option to export mappings as Tiny v2 file

* Comply with JADX's import order conventions

* Only use Java 8 features

* Only use Java 8 features (2)

* Export comments to mappings file

* Method args test (doesn't work)

* Make method arg mapping exports work now

* Use `getTopParentClass()` instead of `getParentClass()`

See https://github.com/skylot/jadx/pull/1505#issuecomment-1145064865

* Remove unneeded method load call

* Small code cleanup; initial (broken) support for method vars

* Fixes regarding inner classes

* Add option to export mappings as Enigma directory

* Add option to export mappings as Enigma file/directory

Temporarily move to my mapping-io fork until this PR gets merged: https://github.com/FabricMC/mapping-io/pull/19

* Fix method vars' lv-indices

* Use correct offset value for method var mappings

* Also supply lvt-index for method var mappings

* Clarify why we're using local mapping-io fork; comment out Fabric Maven for now

* Remove unnecessary `public` modifier

* Make an `if` condition less complicated

* Move mapping export code into jadx-gui (for now)

* Make mapping export async; make export menu only clickable when everything is loaded

* Fix export mappings menu field declaration position
2022-06-11 20:19:08 +01:00
Christian Jones 4edb512121 fix(cli): allow decoding resource-only APKs (#1517)(PR #1530)
* Process resource-only inputs
* Fix error, add testing
2022-06-11 15:40:39 +01:00
Skylot 702b88228c fix(gui): resolve popup menu action run (#1514, #1529) 2022-06-11 15:08:28 +01:00
Skylot 14fd88b2f8 fix(gui): add volatile and update sync for decompiler field in wrapper (#1518) 2022-06-08 21:06:57 +01:00
Skylot 20657e8bb5 doc(cli): improve plugins section formatting 2022-06-06 19:55:58 +01:00
Skylot 93d3194e3b doc: remove incorrect tokei badge 2022-06-06 19:54:15 +01:00
Skylot 39331d9120 fix: remove deprecated --deobf-rewrite-cfg (#1513) 2022-06-06 19:53:47 +01:00
Skylot b4fa6644bc doc: add more badges 2022-06-05 16:06:19 +01:00
Skylot 0b2e2ed034 fix: improve class search for super call (#1512) 2022-06-05 14:49:34 +01:00
Skylot 81231206f3 fix(gui): reset disk cache on new jadx version 2022-06-04 23:26:25 +01:00
Skylot 49d0e76272 fix: support all-catch in multi-catch (#1510) 2022-06-04 23:25:52 +01:00
CmP-lt 0809993b37 fix(gui): improve restoration of windows saved state (PR #1511)
* Fix restoration of windows saved state
* Don't skip restoration of window saved bounds when they intersect with screen bounds.
* Restore saved bounds of main window regardless of it's saved extended state (fixes divider location of main split pane being restored incorrectly when saved state of main window is maximized).
* Add handling for out-of-screen(s) window bounds
2022-06-04 17:41:00 +01:00
Skylot 0c3afcc24c fix(gui): try to prevent jadx node leaks in UI objects 2022-06-03 16:15:14 +01:00
Skylot d6c851eed4 test: fix method code extract 2022-06-02 19:33:16 +01:00
Skylot dcf4a7c4e3 fix(gui): try to resolve some causes of memory leak 2022-06-01 19:48:51 +01:00
Skylot 9ba07b986b fix(gui): reduce usage of nullable decompiler field in jadx wrapper (#1506) 2022-06-01 16:36:30 +01:00
Skylot e6b6b93cbb fix: improve blocks tree compare for finally extract (#1501) 2022-05-31 20:57:34 +01:00
1023 changed files with 47732 additions and 12716 deletions
+13 -1
View File
@@ -7,11 +7,23 @@ insert_final_newline = true
indent_style = tab
tab_width = 4
continuation_indent_size = 8 #IntelliJ Idea specific workaround
charset = utf-8
trim_trailing_whitespace = true
[*.java]
ij_java_continuation_indent_size = 8
ij_java_use_single_class_imports = true
ij_java_class_count_to_use_import_on_demand = 99
ij_java_names_count_to_use_import_on_demand = 99
ij_java_packages_to_use_import_on_demand = *
[*.kt]
ij_kotlin_continuation_indent_size = 8
ij_kotlin_name_count_to_use_star_import = 99
ij_kotlin_name_count_to_use_star_import_for_members = 99
ij_kotlin_packages_to_use_import_on_demand = *
[*.yml]
indent_style = space
indent_size = 2
+12
View File
@@ -0,0 +1,12 @@
* text=auto eol=lf
*.java text eol=lf diff=java
*.kt text eol=lf diff=kotlin
*.kts text eol=lf diff=kotlin
gradlew text eol=lf
*.bat text eol=crlf
*.png binary
*.jar binary
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
@@ -1,19 +0,0 @@
---
name: Decompilation error
about: Create a report to help us improve jadx decompiler
title: "[core]"
labels: Core, bug
assignees: ''
---
**Checks before report**
- check [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A) section on wiki
- search existing issues by exception message
**Describe error**
- full name of method or class with error
- full java stacktrace (no need to copy method fallback code (commented pseudocode))
- **IMPORTANT!** attach or provide link to apk file (double check apk version)
**Note**: GitHub don't allow attach files with `.apk` extension, but you can change extension by adding `.zip` at the end :)
@@ -0,0 +1,40 @@
name: Decompilation issue
description: Create a report to help us improve jadx decompiler
title: '[core] '
labels:
- Core
- bug
body:
- type: markdown
attributes:
value: |
**Checks before submit**
- check [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A) section on wiki
- try [latest unstable build](https://nightly.link/skylot/jadx/workflows/build-artifacts/master), maybe issue already fixed
- search existing issues by exception message
- type: textarea
id: details
attributes:
label: Issue details
placeholder: >-
Describe issue
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output or stacktrace
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: java
- type: textarea
id: sample
attributes:
label: Provide sample and class/method full name
description: |
- sample: attach or provide a link
- full name of class or method with issue
- other details which may help to reproduce issue
- type: input
id: jadx-version
attributes:
label: Jadx version
-10
View File
@@ -1,10 +0,0 @@
---
name: Feature Request
about: Suggest an idea for jadx
title: "[feature]"
labels: new feature
assignees: ''
---
*Describe your idea:*
@@ -0,0 +1,13 @@
name: Feature Request
description: Suggest an idea for jadx
title: '[feature] '
labels:
- 'new feature'
body:
- type: textarea
id: details
attributes:
label: Describe your idea
placeholder: Feature details
validations:
required: true
+43
View File
@@ -0,0 +1,43 @@
name: jadx-gui issue
description: Create a bug report about issue found in jadx-gui
title: '[gui] '
labels:
- GUI
- bug
body:
- type: markdown
attributes:
value: |
**Checks before submit**
- check [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A) section on wiki
- try [latest unstable build](https://nightly.link/skylot/jadx/workflows/build-artifacts/master), maybe issue already fixed
- search existing issues by exception message
- type: textarea
id: details
attributes:
label: Issue details
placeholder: Describe issue and how to reproduce it
validations:
required: true
- type: input
id: jadx-version
attributes:
label: Jadx version
placeholder: check `Help->About`
validations:
required: true
- type: input
id: java-version
attributes:
label: Java version
placeholder: check `Help->About`
validations:
required: true
- type: checkboxes
id: os
attributes:
label: OS
options:
- label: Windows
- label: Linux
- label: macOS
+17 -19
View File
@@ -8,15 +8,15 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'adopt'
java-version: 8
distribution: temurin
java-version: 11
- name: Set jadx version
run: |
@@ -24,15 +24,13 @@ jobs:
JADX_VERSION="${JADX_LAST_TAG:1}.$GITHUB_RUN_NUMBER-${GITHUB_SHA:0:8}"
echo "JADX_VERSION=$JADX_VERSION" >> $GITHUB_ENV
- uses: burrunan/gradle-cache-action@v1
name: Build with Gradle
env:
TERM: dumb
- name: Build with Gradle
uses: gradle/actions/setup-gradle@v3
with:
arguments: clean dist copyExe
arguments: dist copyExe
- name: Save bundle artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ format('jadx-{0}', env.JADX_VERSION) }}
# Waiting fix for https://github.com/actions/upload-artifact/issues/39 to upload zip file
@@ -42,7 +40,7 @@ jobs:
retention-days: 30
- name: Save exe artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ format('jadx-gui-{0}-no-jre-win.exe', env.JADX_VERSION) }}
path: build/*.exe
@@ -52,12 +50,14 @@ jobs:
build-win-bundle:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK
uses: oracle-actions/setup-java@v1 # set latest java version by default
uses: oracle-actions/setup-java@v1
with:
release: 17
- name: Print Java version
shell: bash
@@ -70,15 +70,13 @@ jobs:
JADX_VERSION="${JADX_LAST_TAG:1}.$GITHUB_RUN_NUMBER-${GITHUB_SHA:0:8}"
echo "JADX_VERSION=$JADX_VERSION" >> $GITHUB_ENV
- uses: gradle/gradle-build-action@v2
name: Build with Gradle
env:
TERM: dumb
- name: Build with Gradle
uses: gradle/actions/setup-gradle@v3
with:
arguments: clean dist -PbundleJRE=true
arguments: dist -PbundleJRE=true
- name: Save exe bundle artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ format('jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}
path: jadx-gui/build/*-with-jre-win/*
+12 -12
View File
@@ -8,21 +8,21 @@ on:
jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'adopt'
java-version: 8
distribution: temurin
java-version: 11
- uses: burrunan/gradle-cache-action@v1
name: Build with Gradle
env:
TERM: dumb
- name: Build with Gradle
uses: gradle/actions/setup-gradle@v3
with:
arguments: clean build dist copyExe --warning-mode=all
arguments: build dist copyExe
+3 -3
View File
@@ -25,10 +25,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
queries: +security-extended
languages: ${{ matrix.language }}
@@ -38,4 +38,4 @@ jobs:
./gradlew clean build -x checkstyleTest -x checkstyleMain -x test -x ':jadx-core:testClasses'
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
@@ -1,10 +1,15 @@
name: "Validate Gradle Wrapper"
on: [push]
name: Validate Gradle Wrapper
on:
push:
branches: [ master, build-test ]
pull_request:
branches: [ master ]
jobs:
validation:
name: "Validation"
name: Validation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v3
+1
View File
@@ -22,6 +22,7 @@ classes/
idea/
.gradle/
node_modules/
.vscode/
jadx-output/
*-tmp/
+10 -10
View File
@@ -8,17 +8,17 @@ before_script:
stages:
- test
java-8:
stage: test
image: openjdk:8
script: ./gradlew clean build dist copyExe --warning-mode=all
java-11:
stage: test
image: openjdk:11
script: ./gradlew clean build dist copyExe --warning-mode=all
image: eclipse-temurin:11
script: ./gradlew clean build dist copyExe
java-latest:
java-17:
stage: test
image: openjdk:latest
script: java -version && ./gradlew clean build dist --warning-mode=all
image: eclipse-temurin:17
script: ./gradlew clean build dist copyExe
java-21:
stage: test
image: eclipse-temurin:21
script: ./gradlew clean build dist copyExe
+12
View File
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="jadx-gui" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="jadx.gui.JadxGUI"/>
<module name="jadx.jadx-gui.main"/>
<option name="PROGRAM_PARAMETERS" value="-v"/>
<option name="VM_PARAMETERS"
value="-Xms128M -XX:MaxRAMPercentage=70.0 -Dawt.useSystemAAFontSettings=lcd -Dswing.aatext=true -Djava.util.Arrays.useLegacyMergeSort=true -Djdk.util.zip.disableZip64ExtraFieldValidation=true -XX:+IgnoreUnrecognizedVMOptions --add-opens=java.base/java.lang=ALL-UNNAMED"/>
<method v="2">
<option name="Make" enabled="true"/>
</method>
</configuration>
</component>
+20
View File
@@ -0,0 +1,20 @@
# Config for 'typos' spellchecker (https://github.com/crate-ci/typos)
[default.extend-words]
IPUT = "IPUT"
Laf = "Laf"
Darcula="Darcula"
[default]
extend-ignore-identifiers-re = [
"finaly", # intentional package name
]
[files]
extend-exclude = [
"config/",
"jadx-core/src/main/resources/",
"jadx-core/src/test/",
"jadx-gui/src/main/resources/i18n/",
"!jadx-gui/src/main/resources/i18n/Messages_en_US.properties",
]
+7 -12
View File
@@ -1,32 +1,27 @@
# Contributing
Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project.
Please note, we have [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project.
## Open Issue
1. Before proceed please do:
1. Before proceed, please do:
- check [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A) section on wiki
- search existing issues by exception message
2. Describe error
**Describe error**
2. Describe error:
- full name of method or class with error
- full java stacktrace (no need to copy method fallback code (commented pseudocode))
- **IMPORTANT!:** attach or provide link to apk file (double check apk version)
**Note**: GitHub don't allow attach files with `.apk` extension, but you can change extension by adding `.zip` at the end :)
**Note**: GitHub don't allow attaching files with `.apk` extension, but you can change extension by adding `.zip` at the end :)
## Pull Request Process
1. Please don't submit any code style fixes, dependencies updates or other changes which are not fixing any issues.
1. Please don't submit any code style fixes or dependencies updates changes.
1. Before start working on PR please discuss the change you wish to make via issue. PR without corresponding issue will be rejected.
1. Use only features and API from Java 8 or below.
1. If possible don't add additional dependencies especially if they are big.
1. Use only features and API from Java 11 or below.
1. Make sure your code is correctly formatted, see description here: [Code Formatting](https://github.com/skylot/jadx/wiki/Code-Formatting).
1. Make sure your changes is passing build: `./gradlew clean build dist`
1. Make sure your changes are passing build: `./gradlew clean build dist`
+69 -23
View File
@@ -2,20 +2,25 @@
## JADX
[![Build status](https://github.com/skylot/jadx/workflows/Build/badge.svg)](https://github.com/skylot/jadx/actions?query=workflow%3ABuild)
[![Alerts from lgtm.com](https://img.shields.io/lgtm/alerts/g/skylot/jadx.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/skylot/jadx/alerts/)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
![Build status](https://img.shields.io/github/actions/workflow/status/skylot/jadx/build-artifacts.yml)
![GitHub contributors](https://img.shields.io/github/contributors/skylot/jadx)
![GitHub all releases](https://img.shields.io/github/downloads/skylot/jadx/total)
![GitHub release (latest by SemVer)](https://img.shields.io/github/downloads/skylot/jadx/latest/total)
![Latest release](https://img.shields.io/github/release/skylot/jadx.svg)
[![Maven Central](https://img.shields.io/maven-central/v/io.github.skylot/jadx-core)](https://search.maven.org/search?q=g:io.github.skylot%20AND%20jadx)
![Java 11+](https://img.shields.io/badge/Java-11%2B-blue)
[![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html)
**jadx** - Dex to Java decompiler
Command line and GUI tools for producing Java source code from Android Dex and Apk files
:exclamation::exclamation::exclamation: Please note that in most cases **jadx** can't decompile all 100% of the code, so errors will occur. Check [Troubleshooting guide](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A#decompilation-issues) for workarounds
> [!WARNING]
> Please note that in most cases **jadx** can't decompile all 100% of the code, so errors will occur.<br />
> Check [Troubleshooting guide](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A#decompilation-issues) for workarounds.
**Main features:**
- decompile Dalvik bytecode to java classes from APK, dex, aar, aab and zip files
- decompile Dalvik bytecode to Java code from APK, dex, aar, aab and zip files
- decode `AndroidManifest.xml` and other resources from `resources.arsc`
- deobfuscator included
@@ -35,7 +40,7 @@ See these features in action here: [jadx-gui features overview](https://github.c
### Download
- release
from [github: ![Latest release](https://img.shields.io/github/release/skylot/jadx.svg)](https://github.com/skylot/jadx/releases/latest)
- latest [unstable build](https://nightly.link/skylot/jadx/workflows/build-artifacts/master)
- latest [unstable build ![GitHub commits since tagged version (branch)](https://img.shields.io/github/commits-since/skylot/jadx/latest/master)](https://nightly.link/skylot/jadx/workflows/build-artifacts/master)
After download unpack zip file go to `bin` directory and run:
- `jadx` - command line version
@@ -46,20 +51,24 @@ On Windows run `.bat` files with double-click\
For Windows, you can download it from [oracle.com](https://www.oracle.com/java/technologies/downloads/#jdk17-windows) (select x64 Installer).
### Install
1. Arch linux
1. Arch linux ![Arch Linux package](https://img.shields.io/archlinux/v/extra/any/jadx?label=)
```bash
sudo pacman -S jadx
sudo pacman -S jadx
```
2. macOS
2. macOS ![homebrew version](https://img.shields.io/homebrew/v/jadx?label=)
```bash
brew install jadx
brew install jadx
```
3. [Flathub ![Flathub](https://img.shields.io/flathub/v/com.github.skylot.jadx?label=)](https://flathub.org/apps/details/com.github.skylot.jadx)
```bash
flatpak install flathub com.github.skylot.jadx
```
### Use jadx as a library
You can use jadx in your java projects, check details on [wiki page](https://github.com/skylot/jadx/wiki/Use-jadx-as-a-library)
### Build from source
JDK 8 or higher must be installed:
JDK 11 or higher must be installed:
```
git clone https://github.com/skylot/jadx.git
cd jadx
@@ -73,7 +82,10 @@ and also packed to `build/jadx-<version>.zip`
### Usage
```
jadx[-gui] [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab)
jadx[-gui] [command] [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .jadx.kts)
commands (use '<command> --help' for command options):
plugins - manage jadx plugins
options:
-d, --output-dir - output directory
-ds, --output-dir-src - output directory for sources
@@ -91,26 +103,39 @@ options:
'simple' - simplified instructions (linear, with goto's)
'fallback' - raw instructions without modifications
--show-bad-code - show inconsistent code (incorrectly decompiled)
--no-xml-pretty-print - do not prettify XML
--no-imports - disable use of imports, always write entire package name
--no-debug-info - disable debug info
--no-debug-info - disable debug info parsing and processing
--add-debug-lines - add comments with debug line numbers if available
--no-inline-anonymous - disable anonymous classes inline
--no-inline-methods - disable methods inline
--no-move-inner-classes - disable move inner classes into parent
--no-inline-kotlin-lambda - disable inline for Kotlin lambdas
--no-finally - don't extract finally block
--no-replace-consts - don't replace constant value with matching constant field
--escape-unicode - escape non latin characters in strings (with \u)
--respect-bytecode-access-modifiers - don't change original access modifiers
--mappings-path - deobfuscation mappings file or directory. Allowed formats: Tiny and Tiny v2 (both '.tiny'), Enigma (.mapping) or Enigma directory
--mappings-mode - set mode for handling the deobfuscation mapping file:
'read' - just read, user can always save manually (default)
'read-and-autosave-every-change' - read and autosave after every change
'read-and-autosave-before-closing' - read and autosave before exiting the app or closing the project
'ignore' - don't read or save (can be used to skip loading mapping files referenced in the project file)
--deobf - activate deobfuscation
--deobf-min - min length of name, renamed if shorter, default: 3
--deobf-max - max length of name, renamed if longer, default: 64
--deobf-cfg-file - deobfuscation map file, default: same dir and name as input file with '.jobf' extension
--deobf-cfg-file-mode - set mode for handle deobfuscation map file:
--deobf-whitelist - space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation, default: android.support.v4.* android.support.v7.* android.support.v4.os.* android.support.annotation.Px androidx.core.os.* androidx.annotation.Px
--deobf-cfg-file - deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format), default: same dir and name as input file with '.jobf' extension
--deobf-cfg-file-mode - set mode for handling the JADX auto-generated names' deobfuscation map file:
'read' - read if found, don't save (default)
'read-or-save' - read if found, save otherwise (don't overwrite)
'overwrite' - don't read, always save
'ignore' - don't read and don't save
--deobf-rewrite-cfg - set '--deobf-cfg-file-mode' to 'overwrite' (deprecated)
--deobf-use-sourcename - use source file name as class name alias
--deobf-parse-kotlin-metadata - parse kotlin metadata to class and package names
--deobf-res-name-source - better name source for resources:
'auto' - automatically select best name (default)
'resources' - use resources names
'code' - use R class fields names
--use-kotlin-methods-for-var-names - use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide, default: apply
--rename-flags - fix options (comma-separated list of):
'case' - fix case sensitivity issues (according to --fs-case-sensitive option),
@@ -118,6 +143,10 @@ options:
'printable' - remove non-printable chars from identifiers,
or single 'none' - to disable all renames
or single 'all' - to enable all (default)
--integer-format - how integers are displayed:
'auto' - automatically select (default)
'decimal' - use decimal
'hexadecimal' - use hexadecimal
--fs-case-sensitive - treat filesystem as case sensitive, false by default
--cfg - save methods control flow graph to dot file
--raw-cfg - save methods control flow graph (use raw instructions)
@@ -131,11 +160,28 @@ options:
-h, --help - print this help
Plugin options (-P<name>=<value>):
1) dex-input (Load .dex and .apk files)
-Pdex-input.verify-checksum - Verify dex file checksum before load, values: [yes, no], default: yes
2) java-convert (Convert .jar and .class files to dex)
-Pjava-convert.mode - Convert mode, values: [dx, d8, both], default: both
-Pjava-convert.d8-desugar - Use desugar in d8, values: [yes, no], default: no
1) dex-input: Load .dex and .apk files
- dex-input.verify-checksum - verify dex file checksum before load, values: [yes, no], default: yes
2) java-convert: Convert .class, .jar and .aar files to dex
- java-convert.mode - convert mode, values: [dx, d8, both], default: both
- java-convert.d8-desugar - use desugar in d8, values: [yes, no], default: no
3) kotlin-metadata: Use kotlin.Metadata annotation for code generation
- kotlin-metadata.class-alias - rename class alias, values: [yes, no], default: yes
- kotlin-metadata.method-args - rename function arguments, values: [yes, no], default: yes
- kotlin-metadata.fields - rename fields, values: [yes, no], default: yes
- kotlin-metadata.companion - rename companion object, values: [yes, no], default: yes
- kotlin-metadata.data-class - add data class modifier, values: [yes, no], default: yes
- kotlin-metadata.to-string - rename fields using toString, values: [yes, no], default: yes
- kotlin-metadata.getters - rename simple getters to field names, values: [yes, no], default: yes
4) rename-mappings: various mappings support
- rename-mappings.format - mapping format, values: [AUTO, TINY_FILE, TINY_2_FILE, ENIGMA_FILE, ENIGMA_DIR, SRG_FILE, XSRG_FILE, CSRG_FILE, TSRG_FILE, TSRG_2_FILE, PROGUARD_FILE], default: AUTO
- rename-mappings.invert - invert mapping on load, values: [yes, no], default: no
Environment variables:
JADX_DISABLE_XML_SECURITY - set to 'true' to disable all security checks for XML files
JADX_DISABLE_ZIP_SECURITY - set to 'true' to disable all security checks for zip files
JADX_ZIP_MAX_ENTRIES_COUNT - maximum allowed number of entries in zip files (default: 100 000)
JADX_TMP_DIR - custom temp directory, using system by default
Examples:
jadx -d out classes.dex
@@ -144,7 +190,7 @@ Examples:
jadx --log-level ERROR app.apk
jadx -Pdex-input.verify-checksum=no app.apk
```
These options also worked on jadx-gui running from command line and override options from preferences dialog
These options also work in jadx-gui running from command line and override options from preferences' dialog
### Troubleshooting
Please check wiki page [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A)
-155
View File
@@ -1,155 +0,0 @@
plugins {
id 'com.github.ben-manes.versions' version '0.42.0'
id 'com.diffplug.spotless' version '6.6.1'
}
ext.jadxVersion = System.getenv('JADX_VERSION') ?: "dev"
version = jadxVersion
println("jadx version: ${jadxVersion}")
allprojects {
apply plugin: 'java'
apply plugin: 'checkstyle'
version = jadxVersion
sourceCompatibility = JavaVersion.VERSION_1_8
compileJava {
options.encoding = "UTF-8"
}
jar {
manifest {
mainAttributes('jadx-version': jadxVersion)
}
}
dependencies {
implementation 'org.slf4j:slf4j-api:1.7.36'
compileOnly 'org.jetbrains:annotations:23.0.0'
testImplementation 'ch.qos.logback:logback-classic:1.2.11'
testImplementation 'org.hamcrest:hamcrest-library:2.2'
testImplementation 'org.mockito:mockito-core:4.6.0'
testImplementation 'org.assertj:assertj-core:3.22.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
testCompileOnly 'org.jetbrains:annotations:23.0.0'
}
test {
useJUnitPlatform()
maxParallelForks = Runtime.runtime.availableProcessors()
}
repositories {
mavenLocal()
mavenCentral()
google()
}
}
spotless {
java {
target fileTree(rootDir).matching {
include 'jadx-cli/src/**/java/**/*.java'
include 'jadx-core/src/**/java/**/*.java'
include 'jadx-gui/src/**/java/**/*.java'
include 'jadx-plugins/**/java/**/*.java'
}
importOrderFile 'config/code-formatter/eclipse.importorder'
eclipse().configFile 'config/code-formatter/eclipse.xml'
removeUnusedImports()
lineEndings(com.diffplug.spotless.LineEnding.UNIX)
encoding("UTF-8")
trimTrailingWhitespace()
endWithNewline()
}
format 'misc', {
target '**/*.gradle', '**/*.md', '**/*.xml', '**/.gitignore', '**/.properties'
targetExclude ".gradle/**", ".idea/**", "*/build/**"
lineEndings(com.diffplug.spotless.LineEnding.UNIX)
encoding("UTF-8")
trimTrailingWhitespace()
endWithNewline()
}
}
dependencyUpdates {
resolutionStrategy {
componentSelection { rules ->
rules.all { ComponentSelection selection ->
boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm', 'atlassian'].any { qualifier ->
selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
}
if (rejected) {
selection.reject('Release candidate')
}
}
}
}
}
task copyArtifacts(type: Copy) {
from tasks.getByPath(":jadx-cli:installDist")
from tasks.getByPath(":jadx-gui:installDist")
into layout.buildDirectory.dir("jadx")
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
task pack(type: Zip) {
from copyArtifacts
archiveFileName = "jadx-${jadxVersion}.zip"
destinationDirectory = layout.buildDirectory
}
task copyExe(type: Copy) {
group 'jadx'
description = 'Copy exe to build dir'
mustRunAfter pack // not needed, but gradle throws warning because of same output dir
from tasks.getByPath('jadx-gui:createExe')
include '*.exe'
into layout.buildDirectory
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
task distWinBundle(type: Copy, dependsOn: 'jadx-gui:distWinWithJre') {
group 'jadx'
description = 'Copy bundle to build dir'
destinationDir buildDir
from(tasks.getByPath('jadx-gui:distWinWithJre').outputs) {
include '*.zip'
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
task dist {
group 'jadx'
description = 'Build jadx distribution zip'
dependsOn(pack)
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
if (os.isWindows()) {
if (project.hasProperty("bundleJRE")) {
println("Build win bundle with JRE")
dependsOn('distWinBundle')
} else {
dependsOn('copyExe')
}
}
}
task cleanBuildDir(type: Delete) {
group 'jadx'
delete buildDir
}
clean.dependsOn(cleanBuildDir)
+154
View File
@@ -0,0 +1,154 @@
import com.diffplug.gradle.spotless.FormatExtension
import com.diffplug.gradle.spotless.SpotlessExtension
import com.diffplug.spotless.LineEnding
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
import java.util.Locale
plugins {
id("com.github.ben-manes.versions") version "0.51.0"
id("se.patrikerdes.use-latest-versions") version "0.2.18"
id("com.diffplug.spotless") version "6.25.0"
}
val jadxVersion by extra { System.getenv("JADX_VERSION") ?: "dev" }
println("jadx version: $jadxVersion")
version = jadxVersion
allprojects {
apply(plugin = "java")
apply(plugin = "checkstyle")
apply(plugin = "com.diffplug.spotless")
apply(plugin = "com.github.ben-manes.versions")
apply(plugin = "se.patrikerdes.use-latest-versions")
repositories {
mavenCentral()
}
configure<SpotlessExtension> {
java {
importOrderFile("$rootDir/config/code-formatter/eclipse.importorder")
eclipse().configFile("$rootDir/config/code-formatter/eclipse.xml")
removeUnusedImports()
commonFormatOptions()
}
kotlin {
ktlint().editorConfigOverride(mapOf("indent_style" to "tab"))
commonFormatOptions()
}
kotlinGradle {
ktlint()
commonFormatOptions()
}
format("misc") {
target("**/*.gradle", "**/*.xml", "**/.gitignore", "**/.properties")
targetExclude(".gradle/**", ".idea/**", "*/build/**")
commonFormatOptions()
}
}
tasks.named<DependencyUpdatesTask>("dependencyUpdates") {
rejectVersionIf {
// disallow release candidates as upgradable versions from stable versions
isNonStable(candidate.version) && !isNonStable(currentVersion)
}
}
}
fun FormatExtension.commonFormatOptions() {
lineEndings = LineEnding.UNIX
encoding = Charsets.UTF_8
trimTrailingWhitespace()
endWithNewline()
}
fun isNonStable(version: String): Boolean {
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase(Locale.getDefault()).contains(it) }
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
val isStable = stableKeyword || regex.matches(version)
return isStable.not()
}
val copyArtifacts by tasks.registering(Copy::class) {
val jarCliPattern = "jadx-cli-(.*)-all.jar".toPattern()
from(tasks.getByPath(":jadx-cli:installShadowDist")) {
exclude("**/*.jar")
filter { line ->
jarCliPattern.matcher(line).replaceAll("jadx-$1-all.jar")
.replace("-jar \"\\\"\$CLASSPATH\\\"\"", "-cp \"\\\"\$CLASSPATH\\\"\" jadx.cli.JadxCLI")
.replace("-jar \"%CLASSPATH%\"", "-cp \"%CLASSPATH%\" jadx.cli.JadxCLI")
}
}
val jarGuiPattern = "jadx-gui-(.*)-all.jar".toPattern()
from(tasks.getByPath(":jadx-gui:installShadowDist")) {
exclude("**/*.jar")
filter { line -> jarGuiPattern.matcher(line).replaceAll("jadx-$1-all.jar") }
}
from(tasks.getByPath(":jadx-gui:installShadowDist")) {
include("**/*.jar")
rename("jadx-gui-(.*)-all.jar", "jadx-$1-all.jar")
}
into(layout.buildDirectory.dir("jadx"))
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
val pack by tasks.registering(Zip::class) {
from(copyArtifacts)
archiveFileName.set("jadx-$jadxVersion.zip")
destinationDirectory.set(layout.buildDirectory)
}
val copyExe by tasks.registering(Copy::class) {
group = "jadx"
description = "Copy exe to build dir"
// next task dependencies not needed, but gradle throws warning because of same output dir
mustRunAfter("jar")
mustRunAfter(pack)
from(tasks.getByPath("jadx-gui:createExe"))
include("*.exe")
into(layout.buildDirectory)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
val distWinBundle by tasks.registering(Copy::class) {
group = "jadx"
description = "Copy bundle to build dir"
dependsOn(tasks.getByPath(":jadx-gui:distWinWithJre"))
// next task dependencies not needed, but gradle throws warning because of same output dir
mustRunAfter("jar")
mustRunAfter(pack)
from(tasks.getByPath("jadx-gui:distWinWithJre").outputs) {
include("*.zip")
}
into(layout.buildDirectory)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
val dist by tasks.registering {
group = "jadx"
description = "Build jadx distribution zip"
dependsOn(pack)
val os = DefaultNativePlatform.getCurrentOperatingSystem()
if (os.isWindows) {
if (project.hasProperty("bundleJRE")) {
println("Build win bundle with JRE")
dependsOn(distWinBundle)
} else {
dependsOn(copyExe)
}
}
}
val cleanBuildDir by tasks.registering(Delete::class) {
group = "jadx"
delete(layout.buildDirectory)
}
tasks.getByName("clean").dependsOn(cleanBuildDir)
-3
View File
@@ -1,3 +0,0 @@
plugins {
id 'groovy-gradle-plugin'
}
+11
View File
@@ -0,0 +1,11 @@
plugins {
`kotlin-dsl`
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23")
}
repositories {
gradlePluginPortal()
}
@@ -1,79 +0,0 @@
plugins {
id 'java-library'
id 'maven-publish'
id 'signing'
}
group = 'io.github.skylot'
version = jadxVersion
java {
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = project.name
from components.java
versionMapping {
usage('java-api') {
fromResolutionOf('runtimeClasspath')
}
usage('java-runtime') {
fromResolutionResult()
}
}
pom {
name = project.name
description = 'Dex to Java decompiler'
url = 'https://github.com/skylot/jadx'
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'skylot'
name = 'Skylot'
email = 'skylot@gmail.com'
url = 'https://github.com/skylot'
}
}
scm {
connection = 'scm:git:git://github.com/skylot/jadx.git'
developerConnection = 'scm:git:ssh://github.com:skylot/jadx.git'
url = 'https://github.com/skylot/jadx'
}
}
}
}
repositories {
maven {
def releasesRepoUrl = uri('https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/')
def snapshotsRepoUrl = uri('https://s01.oss.sonatype.org/content/repositories/snapshots/')
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
username = project.properties['ossrhUser'].toString()
password = project.properties['ossrhPassword'].toString()
}
}
}
}
signing {
required { gradle.taskGraph.hasTask("publish") }
sign publishing.publications.mavenJava
}
javadoc {
if (JavaVersion.current().isJava9Compatible()) {
options.addBooleanOption('html5', true)
}
// disable 'missing' warnings
options.addStringOption('Xdoclint:all,-missing', '-quiet')
}
@@ -0,0 +1,57 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
plugins {
java
checkstyle
}
val jadxVersion: String by rootProject.extra
group = "io.github.skylot"
version = jadxVersion
dependencies {
implementation("org.slf4j:slf4j-api:2.0.13")
compileOnly("org.jetbrains:annotations:24.1.0")
testImplementation("ch.qos.logback:logback-classic:1.5.6")
testImplementation("org.hamcrest:hamcrest-library:2.2")
testImplementation("org.mockito:mockito-core:5.11.0")
testImplementation("org.assertj:assertj-core:3.25.3")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testCompileOnly("org.jetbrains:annotations:24.1.0")
}
repositories {
mavenCentral()
// required for: aapt-proto, r8, smali
google()
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
tasks {
compileJava {
options.encoding = "UTF-8"
}
jar {
manifest {
attributes("jadx-version" to jadxVersion)
}
}
test {
useJUnitPlatform()
maxParallelForks = Runtime.getRuntime().availableProcessors()
testLogging {
showExceptions = true
exceptionFormat = TestExceptionFormat.FULL
showCauses = true
}
}
}
@@ -0,0 +1,12 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("jadx-java")
id("org.jetbrains.kotlin.jvm")
}
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
@@ -0,0 +1,81 @@
plugins {
id("jadx-java")
id("java-library")
id("maven-publish")
id("signing")
}
val jadxVersion: String by rootProject.extra
group = "io.github.skylot"
version = jadxVersion
java {
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
artifactId = project.name
from(components["java"])
versionMapping {
usage("java-api") {
fromResolutionOf("runtimeClasspath")
}
usage("java-runtime") {
fromResolutionResult()
}
}
pom {
name.set(project.name)
description.set("Dex to Java decompiler")
url.set("https://github.com/skylot/jadx")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("skylot")
name.set("Skylot")
email.set("skylot@gmail.com")
url.set("https://github.com/skylot")
}
}
scm {
connection .set("scm:git:git://github.com/skylot/jadx.git")
developerConnection.set("scm:git:ssh://github.com:skylot/jadx.git")
url .set("https://github.com/skylot/jadx")
}
}
}
}
repositories {
maven {
val releasesRepoUrl = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
val snapshotsRepoUrl = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
credentials {
username = project.properties["ossrhUser"].toString()
password = project.properties["ossrhPassword"].toString()
}
}
}
}
signing {
isRequired = gradle.taskGraph.hasTask("publish")
sign(publishing.publications["mavenJava"])
}
tasks.javadoc {
val stdOptions = options as StandardJavadocDocletOptions
stdOptions.addBooleanOption("html5", true)
// disable 'missing' warnings
stdOptions.addStringOption("Xdoclint:all,-missing", "-quiet")
}
+222 -170
View File
@@ -1,77 +1,248 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="16">
<profile kind="CodeFormatterProfile" name="jadx eclipse" version="16">
<profiles version="23">
<profile kind="CodeFormatterProfile" name="jadx eclipse" version="23">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="100"/>
@@ -80,267 +251,148 @@
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="140"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+1
View File
@@ -1,5 +1,6 @@
org.gradle.warning.mode=all
org.gradle.parallel=true
org.gradle.caching=true
# Flags for google-java-format (optimize imports by spotless) for Java >= 16.
# Java < 9 will ignore unsupported flags (thanks to -XX:+IgnoreUnrecognizedVMOptions)
Binary file not shown.
+4 -2
View File
@@ -1,6 +1,8 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+28 -13
View File
@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +80,11 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -133,22 +131,29 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
@@ -205,6 +214,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
Vendored
+92 -89
View File
@@ -1,89 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
-30
View File
@@ -1,30 +0,0 @@
plugins {
id 'application'
}
dependencies {
implementation(project(':jadx-core'))
runtimeOnly(project(':jadx-plugins:jadx-dex-input'))
runtimeOnly(project(':jadx-plugins:jadx-java-input'))
runtimeOnly(project(':jadx-plugins:jadx-java-convert'))
runtimeOnly(project(':jadx-plugins:jadx-smali-input'))
implementation 'com.beust:jcommander:1.82'
implementation 'ch.qos.logback:logback-classic:1.2.11'
}
application {
applicationName = 'jadx'
mainClass.set('jadx.cli.JadxCLI')
applicationDefaultJvmArgs = ['-Xms128M', '-XX:MaxRAMPercentage=70.0', '-XX:+UseG1GC']
}
applicationDistribution.with {
into('') {
from '../.'
include 'README.md'
include 'NOTICE'
include 'LICENSE'
}
}
+46
View File
@@ -0,0 +1,46 @@
plugins {
id("jadx-java")
id("application")
// use shadow only for application scripts, jar will be copied from jadx-gui
id("com.github.johnrengelman.shadow") version "8.1.1"
}
dependencies {
implementation(project(":jadx-core"))
implementation(project(":jadx-plugins-tools"))
runtimeOnly(project(":jadx-plugins:jadx-dex-input"))
runtimeOnly(project(":jadx-plugins:jadx-java-input"))
runtimeOnly(project(":jadx-plugins:jadx-java-convert"))
runtimeOnly(project(":jadx-plugins:jadx-smali-input"))
runtimeOnly(project(":jadx-plugins:jadx-rename-mappings"))
runtimeOnly(project(":jadx-plugins:jadx-kotlin-metadata"))
runtimeOnly(project(":jadx-plugins:jadx-script:jadx-script-plugin"))
runtimeOnly(project(":jadx-plugins:jadx-xapk-input"))
implementation("org.jcommander:jcommander:1.83")
implementation("ch.qos.logback:logback-classic:1.5.6")
}
application {
applicationName = "jadx"
mainClass.set("jadx.cli.JadxCLI")
applicationDefaultJvmArgs =
listOf(
"-Xms256M",
"-XX:MaxRAMPercentage=70.0",
// disable zip checks (#1962)
"-Djdk.util.zip.disableZip64ExtraFieldValidation=true",
)
applicationDistribution.from("$rootDir") {
include("README.md")
include("NOTICE")
include("LICENSE")
}
}
tasks.shadowJar {
// shadow jar not needed
configurations = listOf()
}
@@ -18,18 +18,24 @@ import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameterized;
import jadx.api.JadxDecompiler;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.JadxPluginInfo;
import jadx.api.plugins.JadxPluginManager;
import jadx.api.plugins.options.JadxPluginOptions;
import jadx.api.plugins.options.OptionDescription;
import jadx.core.plugins.JadxPluginManager;
import jadx.core.plugins.PluginContext;
import jadx.core.utils.Utils;
import jadx.plugins.tools.JadxExternalPluginsLoader;
public class JCommanderWrapper<T> {
private final JCommander jc;
private final JadxCLIArgs argsObj;
public JCommanderWrapper(T obj) {
this.jc = JCommander.newBuilder().addObject(obj).build();
public JCommanderWrapper(JadxCLIArgs argsObj) {
JCommander.Builder builder = JCommander.newBuilder().addObject(argsObj);
builder.acceptUnknownOptions(true); // workaround for "default" command
JadxCLICommands.append(builder);
this.jc = builder.build();
this.argsObj = argsObj;
}
public boolean parse(String[] args) {
@@ -43,7 +49,15 @@ public class JCommanderWrapper<T> {
}
}
public void overrideProvided(T obj) {
public boolean processCommands() {
String parsedCommand = jc.getParsedCommand();
if (parsedCommand == null) {
return false;
}
return JadxCLICommands.process(this, jc, parsedCommand);
}
public void overrideProvided(JadxCLIArgs obj) {
List<ParameterDescription> fieldsParams = jc.getParameters();
List<ParameterDescription> parameters = new ArrayList<>(1 + fieldsParams.size());
parameters.add(jc.getMainParameterValue());
@@ -70,13 +84,50 @@ public class JCommanderWrapper<T> {
return value;
}
public List<String> getUnknownOptions() {
return jc.getUnknownOptions();
}
public void printUsage() {
// print usage in not sorted fields order (by default its sorted by description)
LogHelper.setLogLevel(LogHelper.LogLevelEnum.ERROR); // mute logger while printing help
// print usage in not sorted fields order (by default sorted by description)
PrintStream out = System.out;
out.println();
out.println("jadx - dex to java decompiler, version: " + JadxDecompiler.getVersion());
out.println();
out.println("usage: jadx [options] " + jc.getMainParameterDescription());
out.println("usage: jadx [command] [options] " + jc.getMainParameterDescription());
out.println("commands (use '<command> --help' for command options):");
for (String command : jc.getCommands().keySet()) {
out.println(" " + command + "\t - " + jc.getUsageFormatter().getCommandDescription(command));
}
out.println();
int maxNamesLen = printOptions(jc, out, true);
out.println(appendPluginOptions(maxNamesLen));
out.println();
out.println("Environment variables:");
out.println(" JADX_DISABLE_XML_SECURITY - set to 'true' to disable all security checks for XML files");
out.println(" JADX_DISABLE_ZIP_SECURITY - set to 'true' to disable all security checks for zip files");
out.println(" JADX_ZIP_MAX_ENTRIES_COUNT - maximum allowed number of entries in zip files (default: 100 000)");
out.println(" JADX_TMP_DIR - custom temp directory, using system by default");
out.println();
out.println("Examples:");
out.println(" jadx -d out classes.dex");
out.println(" jadx --rename-flags \"none\" classes.dex");
out.println(" jadx --rename-flags \"valid, printable\" classes.dex");
out.println(" jadx --log-level ERROR app.apk");
out.println(" jadx -Pdex-input.verify-checksum=no app.apk");
}
public void printUsage(JCommander subCommander) {
PrintStream out = System.out;
out.println("usage: " + subCommander.getProgramName() + " [options]");
printOptions(subCommander, out, false);
}
private static int printOptions(JCommander jc, PrintStream out, boolean addDefaults) {
out.println("options:");
List<ParameterDescription> params = jc.getParameters();
@@ -91,7 +142,7 @@ public class JCommanderWrapper<T> {
}
maxNamesLen += 3;
JadxCLIArgs args = (JadxCLIArgs) jc.getObjects().get(0);
Object args = jc.getObjects().get(0);
for (Field f : getFields(args.getClass())) {
String name = f.getName();
ParameterDescription p = paramsMap.get(name);
@@ -113,26 +164,21 @@ public class JCommanderWrapper<T> {
} else {
opt.append("- ").append(description);
}
String defaultValue = getDefaultValue(args, f, opt);
if (defaultValue != null && !description.contains("(default)")) {
opt.append(", default: ").append(defaultValue);
if (addDefaults) {
String defaultValue = getDefaultValue(args, f);
if (defaultValue != null && !description.contains("(default)")) {
opt.append(", default: ").append(defaultValue);
}
}
out.println(opt);
}
out.println(appendPluginOptions(maxNamesLen));
out.println();
out.println("Examples:");
out.println(" jadx -d out classes.dex");
out.println(" jadx --rename-flags \"none\" classes.dex");
out.println(" jadx --rename-flags \"valid, printable\" classes.dex");
out.println(" jadx --log-level ERROR app.apk");
out.println(" jadx -Pdex-input.verify-checksum=no app.apk");
return maxNamesLen;
}
/**
* Get all declared fields of the specified class and all super classes
*/
private List<Field> getFields(Class<?> clazz) {
private static List<Field> getFields(Class<?> clazz) {
List<Field> fieldList = new ArrayList<>();
while (clazz != null) {
fieldList.addAll(Arrays.asList(clazz.getDeclaredFields()));
@@ -142,7 +188,7 @@ public class JCommanderWrapper<T> {
}
@Nullable
private String getDefaultValue(JadxCLIArgs args, Field f, StringBuilder opt) {
private static String getDefaultValue(Object args, Field f) {
try {
Class<?> fieldType = f.getType();
if (fieldType == int.class) {
@@ -171,13 +217,18 @@ public class JCommanderWrapper<T> {
private String appendPluginOptions(int maxNamesLen) {
StringBuilder sb = new StringBuilder();
JadxPluginManager pluginManager = new JadxPluginManager();
pluginManager.load();
int k = 1;
for (JadxPlugin plugin : pluginManager.getAllPlugins()) {
if (plugin instanceof JadxPluginOptions) {
if (appendPlugin(((JadxPluginOptions) plugin), sb, maxNamesLen, k)) {
k++;
// load and init all options plugins to print all options
try (JadxDecompiler decompiler = new JadxDecompiler(argsObj.toJadxArgs())) {
JadxPluginManager pluginManager = decompiler.getPluginManager();
pluginManager.load(new JadxExternalPluginsLoader());
pluginManager.initAll();
for (PluginContext context : pluginManager.getAllPluginContexts()) {
JadxPluginOptions options = context.getOptions();
if (options != null) {
if (appendPlugin(context.getPluginInfo(), context.getOptions(), sb, maxNamesLen, k)) {
k++;
}
}
}
}
@@ -187,17 +238,16 @@ public class JCommanderWrapper<T> {
return "\nPlugin options (-P<name>=<value>):" + sb;
}
private boolean appendPlugin(JadxPluginOptions plugin, StringBuilder out, int maxNamesLen, int k) {
List<OptionDescription> descs = plugin.getOptionsDescriptions();
private boolean appendPlugin(JadxPluginInfo pluginInfo, JadxPluginOptions options, StringBuilder out, int maxNamesLen, int k) {
List<OptionDescription> descs = options.getOptionsDescriptions();
if (descs.isEmpty()) {
return false;
}
JadxPluginInfo pluginInfo = plugin.getPluginInfo();
out.append("\n ").append(k).append(") ");
out.append(pluginInfo.getPluginId()).append(" (").append(pluginInfo.getDescription()).append(") ");
out.append("\n ").append(k).append(") ");
out.append(pluginInfo.getPluginId()).append(": ").append(pluginInfo.getDescription());
for (OptionDescription desc : descs) {
StringBuilder opt = new StringBuilder();
opt.append(" -P").append(desc.name());
opt.append(" - ").append(desc.name());
addSpaces(opt, maxNamesLen - opt.length());
opt.append("- ").append(desc.description());
if (!desc.values().isEmpty()) {
+25 -3
View File
@@ -5,11 +5,13 @@ import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.impl.AnnotatedCodeWriter;
import jadx.api.impl.NoOpCodeCache;
import jadx.api.impl.SimpleCodeWriter;
import jadx.cli.LogHelper.LogLevelEnum;
import jadx.core.utils.exceptions.JadxArgsValidateException;
import jadx.core.utils.files.FileUtils;
import jadx.plugins.tools.JadxExternalPluginsLoader;
public class JadxCLI {
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
@@ -43,7 +45,8 @@ public class JadxCLI {
LogHelper.setLogLevelsForLoadingStage();
JadxArgs jadxArgs = cliArgs.toJadxArgs();
jadxArgs.setCodeCache(new NoOpCodeCache());
jadxArgs.setCodeWriterProvider(SimpleCodeWriter::new);
jadxArgs.setPluginLoader(new JadxExternalPluginsLoader());
initCodeWriterProvider(jadxArgs);
try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
jadx.load();
if (checkForErrors(jadx)) {
@@ -64,10 +67,28 @@ public class JadxCLI {
return 0;
}
private static void initCodeWriterProvider(JadxArgs jadxArgs) {
switch (jadxArgs.getOutputFormat()) {
case JAVA:
jadxArgs.setCodeWriterProvider(SimpleCodeWriter::new);
break;
case JSON:
// needed for code offsets and source lines
jadxArgs.setCodeWriterProvider(AnnotatedCodeWriter::new);
break;
}
}
private static boolean checkForErrors(JadxDecompiler jadx) {
if (jadx.getRoot().getClasses().isEmpty()) {
LOG.error("Load failed! No classes for decompile!");
return true;
if (jadx.getArgs().isSkipResources()) {
LOG.error("Load failed! No classes for decompile!");
return true;
}
if (!jadx.getArgs().isSkipSources()) {
LOG.warn("No classes to decompile; decoding resources only");
jadx.getArgs().setSkipSources(true);
}
}
if (jadx.getErrorsCount() > 0) {
LOG.error("Load with errors! Check log for details");
@@ -81,6 +102,7 @@ public class JadxCLI {
if (LogHelper.getLogLevel() == LogLevelEnum.QUIET) {
jadx.save();
} else {
LOG.info("processing ...");
jadx.save(500, (done, total) -> {
int progress = (int) (done * 100.0 / total);
System.out.printf("INFO - progress: %d of %d (%d%%)\r", done, total, progress);
+184 -75
View File
@@ -1,12 +1,16 @@
package jadx.cli;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -20,13 +24,17 @@ import jadx.api.JadxArgs;
import jadx.api.JadxArgs.RenameEnum;
import jadx.api.JadxArgs.UseKotlinMethodsForVarNames;
import jadx.api.JadxDecompiler;
import jadx.api.args.DeobfuscationMapFileMode;
import jadx.core.utils.exceptions.JadxException;
import jadx.api.args.GeneratedRenamesMappingFileMode;
import jadx.api.args.IntegerFormat;
import jadx.api.args.ResourceNameSource;
import jadx.api.args.UserRenamesMappingsMode;
import jadx.core.deobf.conditions.DeobfWhitelist;
import jadx.core.utils.exceptions.JadxArgsValidateException;
import jadx.core.utils.files.FileUtils;
public class JadxCLIArgs {
@Parameter(description = "<input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab)")
@Parameter(description = "<input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .jadx.kts)")
protected List<String> files = new ArrayList<>(1);
@Parameter(names = { "-d", "--output-dir" }, description = "output directory")
@@ -73,10 +81,13 @@ public class JadxCLIArgs {
@Parameter(names = { "--show-bad-code" }, description = "show inconsistent code (incorrectly decompiled)")
protected boolean showInconsistentCode = false;
@Parameter(names = { "--no-xml-pretty-print" }, description = "do not prettify XML")
protected boolean skipXmlPrettyPrint = false;
@Parameter(names = { "--no-imports" }, description = "disable use of imports, always write entire package name")
protected boolean useImports = true;
@Parameter(names = { "--no-debug-info" }, description = "disable debug info")
@Parameter(names = { "--no-debug-info" }, description = "disable debug info parsing and processing")
protected boolean debugInfo = true;
@Parameter(names = { "--add-debug-lines" }, description = "add comments with debug line numbers if available")
@@ -88,6 +99,15 @@ public class JadxCLIArgs {
@Parameter(names = { "--no-inline-methods" }, description = "disable methods inline")
protected boolean inlineMethods = true;
@Parameter(names = { "--no-move-inner-classes" }, description = "disable move inner classes into parent")
protected boolean moveInnerClasses = true;
@Parameter(names = { "--no-inline-kotlin-lambda" }, description = "disable inline for Kotlin lambdas")
protected boolean allowInlineKotlinLambda = true;
@Parameter(names = "--no-finally", description = "don't extract finally block")
protected boolean extractFinally = true;
@Parameter(names = "--no-replace-consts", description = "don't replace constant value with matching constant field")
protected boolean replaceConsts = true;
@@ -97,6 +117,22 @@ public class JadxCLIArgs {
@Parameter(names = { "--respect-bytecode-access-modifiers" }, description = "don't change original access modifiers")
protected boolean respectBytecodeAccessModifiers = false;
@Parameter(
names = { "--mappings-path" },
description = "deobfuscation mappings file or directory. Allowed formats: Tiny and Tiny v2 (both '.tiny'), Enigma (.mapping) or Enigma directory"
)
protected Path userRenamesMappingsPath;
@Parameter(
names = { "--mappings-mode" },
description = "set mode for handling the deobfuscation mapping file:"
+ "\n 'read' - just read, user can always save manually (default)"
+ "\n 'read-and-autosave-every-change' - read and autosave after every change"
+ "\n 'read-and-autosave-before-closing' - read and autosave before exiting the app or closing the project"
+ "\n 'ignore' - don't read or save (can be used to skip loading mapping files referenced in the project file)"
)
protected UserRenamesMappingsMode userRenamesMappingsMode = UserRenamesMappingsMode.getDefault();
@Parameter(names = { "--deobf" }, description = "activate deobfuscation")
protected boolean deobfuscationOn = false;
@@ -107,30 +143,41 @@ public class JadxCLIArgs {
protected int deobfuscationMaxLength = 64;
@Parameter(
names = { "--deobf-cfg-file" },
description = "deobfuscation map file, default: same dir and name as input file with '.jobf' extension"
names = { "--deobf-whitelist" },
description = "space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation"
)
protected String deobfuscationMapFile;
protected String deobfuscationWhitelistStr = DeobfWhitelist.DEFAULT_STR;
@Parameter(
names = { "--deobf-cfg-file" },
description = "deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format),"
+ " default: same dir and name as input file with '.jobf' extension"
)
protected String generatedRenamesMappingFile;
@Parameter(
names = { "--deobf-cfg-file-mode" },
description = "set mode for handle deobfuscation map file:"
description = "set mode for handling the JADX auto-generated names' deobfuscation map file:"
+ "\n 'read' - read if found, don't save (default)"
+ "\n 'read-or-save' - read if found, save otherwise (don't overwrite)"
+ "\n 'overwrite' - don't read, always save"
+ "\n 'ignore' - don't read and don't save",
converter = DeobfuscationMapFileModeConverter.class
)
protected DeobfuscationMapFileMode deobfuscationMapFileMode = DeobfuscationMapFileMode.READ;
@Parameter(names = { "--deobf-rewrite-cfg" }, description = "set '--deobf-cfg-file-mode' to 'overwrite' (deprecated)")
protected boolean deobfuscationForceSave = false;
protected GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.getDefault();
@Parameter(names = { "--deobf-use-sourcename" }, description = "use source file name as class name alias")
protected boolean deobfuscationUseSourceNameAsAlias = false;
@Parameter(names = { "--deobf-parse-kotlin-metadata" }, description = "parse kotlin metadata to class and package names")
protected boolean deobfuscationParseKotlinMetadata = false;
@Parameter(
names = { "--deobf-res-name-source" },
description = "better name source for resources:"
+ "\n 'auto' - automatically select best name (default)"
+ "\n 'resources' - use resources names"
+ "\n 'code' - use R class fields names",
converter = ResourceNameSourceConverter.class
)
protected ResourceNameSource resourceNameSource = ResourceNameSource.AUTO;
@Parameter(
names = { "--use-kotlin-methods-for-var-names" },
@@ -151,6 +198,16 @@ public class JadxCLIArgs {
)
protected Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class);
@Parameter(
names = { "--integer-format" },
description = "how integers are displayed:"
+ "\n 'auto' - automatically select (default)"
+ "\n 'decimal' - use decimal"
+ "\n 'hexadecimal' - use hexadecimal",
converter = IntegerFormatConverter.class
)
protected IntegerFormat integerFormat = IntegerFormat.AUTO;
@Parameter(names = { "--fs-case-sensitive" }, description = "treat filesystem as case sensitive, false by default")
protected boolean fsCaseSensitive = false;
@@ -176,7 +233,7 @@ public class JadxCLIArgs {
@Parameter(
names = { "--log-level" },
description = "set log level, values: quiet, progress, error, warn, info, debug",
converter = LogHelper.LogLevelConverter.class
converter = LogLevelConverter.class
)
protected LogHelper.LogLevelEnum logLevel = LogHelper.LogLevelEnum.PROGRESS;
@@ -218,6 +275,10 @@ public class JadxCLIArgs {
}
private boolean process(JCommanderWrapper<JadxCLIArgs> jcw) {
files.addAll(jcw.getUnknownOptions());
if (jcw.processCommands()) {
return false;
}
if (printHelp) {
jcw.printUsage();
return false;
@@ -226,14 +287,13 @@ public class JadxCLIArgs {
System.out.println(JadxDecompiler.getVersion());
return false;
}
try {
if (threadsCount <= 0) {
throw new JadxException("Threads count must be positive, got: " + threadsCount);
if (threadsCount <= 0) {
throw new JadxArgsValidateException("Threads count must be positive, got: " + threadsCount);
}
for (String fileName : files) {
if (fileName.startsWith("-")) {
throw new JadxArgsValidateException("Unknown option: " + fileName);
}
} catch (JadxException e) {
System.err.println("ERROR: " + e.getMessage());
jcw.printUsage();
return false;
}
return true;
}
@@ -257,29 +317,35 @@ public class JadxCLIArgs {
args.setCfgOutput(cfgOutput);
args.setRawCFGOutput(rawCfgOutput);
args.setReplaceConsts(replaceConsts);
args.setDeobfuscationOn(deobfuscationOn);
args.setDeobfuscationMapFile(FileUtils.toFile(deobfuscationMapFile));
if (deobfuscationForceSave) {
args.setDeobfuscationMapFileMode(DeobfuscationMapFileMode.OVERWRITE);
} else {
args.setDeobfuscationMapFileMode(deobfuscationMapFileMode);
if (userRenamesMappingsPath != null) {
args.setUserRenamesMappingsPath(userRenamesMappingsPath);
}
args.setUserRenamesMappingsMode(userRenamesMappingsMode);
args.setDeobfuscationOn(deobfuscationOn);
args.setGeneratedRenamesMappingFile(FileUtils.toFile(generatedRenamesMappingFile));
args.setGeneratedRenamesMappingFileMode(generatedRenamesMappingFileMode);
args.setDeobfuscationMinLength(deobfuscationMinLength);
args.setDeobfuscationMaxLength(deobfuscationMaxLength);
args.setDeobfuscationWhitelist(Arrays.asList(deobfuscationWhitelistStr.split(" ")));
args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias);
args.setParseKotlinMetadata(deobfuscationParseKotlinMetadata);
args.setUseKotlinMethodsForVarNames(useKotlinMethodsForVarNames);
args.setResourceNameSource(resourceNameSource);
args.setEscapeUnicode(escapeUnicode);
args.setRespectBytecodeAccModifiers(respectBytecodeAccessModifiers);
args.setExportAsGradleProject(exportAsGradleProject);
args.setSkipXmlPrettyPrint(skipXmlPrettyPrint);
args.setUseImports(useImports);
args.setDebugInfo(debugInfo);
args.setInsertDebugLines(addDebugLines);
args.setInlineAnonymousClasses(inlineAnonymousClasses);
args.setInlineMethods(inlineMethods);
args.setMoveInnerClasses(moveInnerClasses);
args.setAllowInlineKotlinLambda(allowInlineKotlinLambda);
args.setExtractFinally(extractFinally);
args.setRenameFlags(renameFlags);
args.setFsCaseSensitive(fsCaseSensitive);
args.setCommentsLevel(commentsLevel);
args.setIntegerFormat(integerFormat);
args.setUseDxInput(useDx);
args.setPluginOptions(pluginOptions);
return args;
@@ -357,6 +423,26 @@ public class JadxCLIArgs {
return inlineMethods;
}
public boolean isMoveInnerClasses() {
return moveInnerClasses;
}
public boolean isAllowInlineKotlinLambda() {
return allowInlineKotlinLambda;
}
public boolean isExtractFinally() {
return extractFinally;
}
public Path getUserRenamesMappingsPath() {
return userRenamesMappingsPath;
}
public UserRenamesMappingsMode getUserRenamesMappingsMode() {
return userRenamesMappingsMode;
}
public boolean isDeobfuscationOn() {
return deobfuscationOn;
}
@@ -369,30 +455,34 @@ public class JadxCLIArgs {
return deobfuscationMaxLength;
}
public String getDeobfuscationMapFile() {
return deobfuscationMapFile;
public String getDeobfuscationWhitelistStr() {
return deobfuscationWhitelistStr;
}
public DeobfuscationMapFileMode getDeobfuscationMapFileMode() {
return deobfuscationMapFileMode;
public String getGeneratedRenamesMappingFile() {
return generatedRenamesMappingFile;
}
public boolean isDeobfuscationForceSave() {
return deobfuscationForceSave;
public GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {
return generatedRenamesMappingFileMode;
}
public boolean isDeobfuscationUseSourceNameAsAlias() {
return deobfuscationUseSourceNameAsAlias;
}
public boolean isDeobfuscationParseKotlinMetadata() {
return deobfuscationParseKotlinMetadata;
public ResourceNameSource getResourceNameSource() {
return resourceNameSource;
}
public UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() {
return useKotlinMethodsForVarNames;
}
public IntegerFormat getIntegerFormat() {
return integerFormat;
}
public boolean isEscapeUnicode() {
return escapeUnicode;
}
@@ -417,6 +507,10 @@ public class JadxCLIArgs {
return exportAsGradleProject;
}
public boolean isSkipXmlPrettyPrint() {
return skipXmlPrettyPrint;
}
public boolean isRenameCaseSensitive() {
return renameFlags.contains(RenameEnum.CASE);
}
@@ -464,8 +558,8 @@ public class JadxCLIArgs {
for (String s : value.split(",")) {
try {
set.add(RenameEnum.valueOf(s.trim().toUpperCase(Locale.ROOT)));
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
} catch (Exception e) {
throw new JadxArgsValidateException(
'\'' + s + "' is unknown for parameter " + paramName
+ ", possible values are " + enumValuesString(RenameEnum.values()));
}
@@ -474,54 +568,64 @@ public class JadxCLIArgs {
}
}
public static class CommentsLevelConverter implements IStringConverter<CommentsLevel> {
@Override
public CommentsLevel convert(String value) {
try {
return CommentsLevel.valueOf(value.toUpperCase());
} catch (Exception e) {
throw new IllegalArgumentException(
'\'' + value + "' is unknown comments level, possible values are: "
+ JadxCLIArgs.enumValuesString(CommentsLevel.values()));
}
public static class CommentsLevelConverter extends BaseEnumConverter<CommentsLevel> {
public CommentsLevelConverter() {
super(CommentsLevel::valueOf, CommentsLevel::values);
}
}
public static class UseKotlinMethodsForVarNamesConverter implements IStringConverter<UseKotlinMethodsForVarNames> {
@Override
public UseKotlinMethodsForVarNames convert(String value) {
try {
return UseKotlinMethodsForVarNames.valueOf(value.replace('-', '_').toUpperCase());
} catch (Exception e) {
throw new IllegalArgumentException(
'\'' + value + "' is unknown, possible values are: "
+ JadxCLIArgs.enumValuesString(CommentsLevel.values()));
}
public static class UseKotlinMethodsForVarNamesConverter extends BaseEnumConverter<UseKotlinMethodsForVarNames> {
public UseKotlinMethodsForVarNamesConverter() {
super(UseKotlinMethodsForVarNames::valueOf, UseKotlinMethodsForVarNames::values);
}
}
public static class DeobfuscationMapFileModeConverter implements IStringConverter<DeobfuscationMapFileMode> {
@Override
public DeobfuscationMapFileMode convert(String value) {
try {
return DeobfuscationMapFileMode.valueOf(value.toUpperCase());
} catch (Exception e) {
throw new IllegalArgumentException(
'\'' + value + "' is unknown, possible values are: "
+ JadxCLIArgs.enumValuesString(DeobfuscationMapFileMode.values()));
}
public static class DeobfuscationMapFileModeConverter extends BaseEnumConverter<GeneratedRenamesMappingFileMode> {
public DeobfuscationMapFileModeConverter() {
super(GeneratedRenamesMappingFileMode::valueOf, GeneratedRenamesMappingFileMode::values);
}
}
public static class DecompilationModeConverter implements IStringConverter<DecompilationMode> {
public static class ResourceNameSourceConverter extends BaseEnumConverter<ResourceNameSource> {
public ResourceNameSourceConverter() {
super(ResourceNameSource::valueOf, ResourceNameSource::values);
}
}
public static class DecompilationModeConverter extends BaseEnumConverter<DecompilationMode> {
public DecompilationModeConverter() {
super(DecompilationMode::valueOf, DecompilationMode::values);
}
}
public static class LogLevelConverter extends BaseEnumConverter<LogHelper.LogLevelEnum> {
public LogLevelConverter() {
super(LogHelper.LogLevelEnum::valueOf, LogHelper.LogLevelEnum::values);
}
}
public static class IntegerFormatConverter extends BaseEnumConverter<IntegerFormat> {
public IntegerFormatConverter() {
super(IntegerFormat::valueOf, IntegerFormat::values);
}
}
public abstract static class BaseEnumConverter<E extends Enum<E>> implements IStringConverter<E> {
private final Function<String, E> parse;
private final Supplier<E[]> values;
public BaseEnumConverter(Function<String, E> parse, Supplier<E[]> values) {
this.parse = parse;
this.values = values;
}
@Override
public DecompilationMode convert(String value) {
public E convert(String value) {
try {
return DecompilationMode.valueOf(value.toUpperCase());
return parse.apply(stringAsEnumName(value));
} catch (Exception e) {
throw new IllegalArgumentException(
'\'' + value + "' is unknown, possible values are: "
+ JadxCLIArgs.enumValuesString(DecompilationMode.values()));
throw new JadxArgsValidateException(
'\'' + value + "' is unknown, possible values are: " + enumValuesString(values.get()));
}
}
}
@@ -531,4 +635,9 @@ public class JadxCLIArgs {
.map(v -> v.name().replace('_', '-').toLowerCase(Locale.ROOT))
.collect(Collectors.joining(", "));
}
private static String stringAsEnumName(String value) {
// inverse of enumValuesString conversion
return value.replace('-', '_').toUpperCase(Locale.ROOT);
}
}
@@ -0,0 +1,37 @@
package jadx.cli;
import java.util.Map;
import java.util.TreeMap;
import com.beust.jcommander.JCommander;
import jadx.cli.commands.CommandPlugins;
import jadx.cli.commands.ICommand;
import jadx.core.utils.exceptions.JadxArgsValidateException;
public class JadxCLICommands {
private static final Map<String, ICommand> COMMANDS_MAP = new TreeMap<>();
static {
JadxCLICommands.register(new CommandPlugins());
}
public static void register(ICommand command) {
COMMANDS_MAP.put(command.name(), command);
}
public static void append(JCommander.Builder builder) {
COMMANDS_MAP.forEach(builder::addCommand);
}
public static boolean process(JCommanderWrapper<?> jcw, JCommander jc, String parsedCommand) {
ICommand command = COMMANDS_MAP.get(parsedCommand);
if (command == null) {
throw new JadxArgsValidateException("Unknown command: " + parsedCommand
+ ". Expected one of: " + COMMANDS_MAP.keySet());
}
JCommander subCommander = jc.getCommands().get(parsedCommand);
command.process(jcw, subCommander);
return true;
}
}
+5 -16
View File
@@ -4,8 +4,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.LoggerFactory;
import com.beust.jcommander.IStringConverter;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
@@ -53,6 +51,11 @@ public class LogHelper {
return args.logLevel;
}
public static void setLogLevel(LogLevelEnum newLogLevel) {
logLevelValue = newLogLevel;
applyLogLevel(logLevelValue);
}
public static void setLogLevelsForLoadingStage() {
if (logLevelValue == null) {
return;
@@ -119,18 +122,4 @@ public class LogHelper {
}
return false;
}
public static class LogLevelConverter implements IStringConverter<LogLevelEnum> {
@Override
public LogLevelEnum convert(String value) {
try {
return LogLevelEnum.valueOf(value.toUpperCase());
} catch (Exception e) {
throw new IllegalArgumentException(
'\'' + value + "' is unknown log level, possible values are "
+ JadxCLIArgs.enumValuesString(LogLevelEnum.values()));
}
}
}
}
@@ -12,6 +12,7 @@ import jadx.api.JadxDecompiler;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.visitors.SaveCode;
import jadx.core.utils.exceptions.JadxArgsValidateException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
@@ -33,10 +34,10 @@ public class SingleClassMode {
.findFirst().orElse(null);
}
if (clsForProcess == null) {
throw new JadxRuntimeException("Input class not found: " + singleClass);
throw new JadxArgsValidateException("Input class not found: " + singleClass);
}
if (clsForProcess.contains(AFlag.DONT_GENERATE)) {
throw new JadxRuntimeException("Input class can't be saved by currect jadx settings (marked as DONT_GENERATE)");
throw new JadxArgsValidateException("Input class can't be saved by current jadx settings (marked as DONT_GENERATE)");
}
if (clsForProcess.isInner()) {
clsForProcess = clsForProcess.getTopParentClass();
@@ -52,7 +53,7 @@ public class SingleClassMode {
if (size == 1) {
clsForProcess = classes.get(0);
} else {
throw new JadxRuntimeException("Found " + size + " classes, single class output can't be used");
throw new JadxArgsValidateException("Found " + size + " classes, single class output can't be used");
}
}
ICodeInfo codeInfo;
@@ -2,7 +2,6 @@ package jadx.cli.clst;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;
@@ -12,13 +11,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs;
import jadx.api.plugins.JadxPluginManager;
import jadx.api.plugins.input.JadxInputPlugin;
import jadx.api.plugins.input.data.ILoadResult;
import jadx.api.JadxDecompiler;
import jadx.core.clsp.ClsSet;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.SignatureProcessor;
import jadx.core.utils.files.FileUtils;
/**
* Utility class for convert dex or jar to jadx classes set (.jcst)
@@ -27,41 +23,50 @@ public class ConvertToClsSet {
private static final Logger LOG = LoggerFactory.getLogger(ConvertToClsSet.class);
public static void usage() {
LOG.info("<output .jcst or .jar file> <several input dex or jar files> ");
LOG.info("<android API level (number)> <output .jcst file> <several input dex or jar files> ");
LOG.info("Arguments to update core.jcst: "
+ "<android API level (number)> "
+ "<jadx root>/jadx-core/src/main/resources/clst/core.jcst "
+ "<sdk_root>/platforms/android-<api level>/android.jar"
+ "<sdk_root>/platforms/android-<api level>/optional/android.car.jar "
+ "<sdk_root>/platforms/android-<api level>/optional/org.apache.http.legacy.jar");
}
public static void main(String[] args) throws Exception {
if (args.length < 2) {
public static void main(String[] args) {
if (args.length != 5) {
usage();
System.exit(1);
}
List<Path> inputPaths = Stream.of(args).map(Paths::get).collect(Collectors.toList());
int androidApiLevel = Integer.parseInt(args[0]);
List<Path> inputPaths = Stream.of(args).skip(1).map(Paths::get).collect(Collectors.toList());
Path output = inputPaths.remove(0);
JadxPluginManager pluginManager = new JadxPluginManager();
pluginManager.load();
List<ILoadResult> loadedInputs = new ArrayList<>();
for (JadxInputPlugin inputPlugin : pluginManager.getInputPlugins()) {
loadedInputs.add(inputPlugin.loadFiles(inputPaths));
}
JadxArgs jadxArgs = new JadxArgs();
jadxArgs.setInputFiles(FileUtils.toFiles(inputPaths));
// disable not needed passes executed at prepare stage
jadxArgs.setDeobfuscationOn(false);
jadxArgs.setRenameFlags(EnumSet.noneOf(JadxArgs.RenameEnum.class));
RootNode root = new RootNode(jadxArgs);
root.loadClasses(loadedInputs);
jadxArgs.setUseSourceNameAsClassAlias(false);
jadxArgs.setMoveInnerClasses(false);
jadxArgs.setInlineAnonymousClasses(false);
jadxArgs.setInlineMethods(false);
// from pre-decompilation stage run only SignatureProcessor
SignatureProcessor signatureProcessor = new SignatureProcessor();
signatureProcessor.init(root);
for (ClassNode classNode : root.getClasses()) {
signatureProcessor.visit(classNode);
// don't require/load class set file
jadxArgs.setLoadJadxClsSetFile(false);
try (JadxDecompiler decompiler = new JadxDecompiler(jadxArgs)) {
decompiler.load();
RootNode root = decompiler.getRoot();
ClsSet set = new ClsSet(root);
set.setAndroidApiLevel(androidApiLevel);
set.loadFrom(root);
set.save(output);
LOG.info("Output: {}", output);
LOG.info("done");
} catch (Exception e) {
LOG.error("Failed with error", e);
}
ClsSet set = new ClsSet(root);
set.loadFrom(root);
set.save(output);
LOG.info("Output: {}", output);
LOG.info("done");
}
}
@@ -0,0 +1,98 @@
package jadx.cli.commands;
import java.util.List;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import jadx.cli.JCommanderWrapper;
import jadx.plugins.tools.JadxPluginsList;
import jadx.plugins.tools.JadxPluginsTools;
import jadx.plugins.tools.data.JadxPluginMetadata;
import jadx.plugins.tools.data.JadxPluginUpdate;
@Parameters(commandDescription = "manage jadx plugins")
public class CommandPlugins implements ICommand {
@Parameter(names = { "-i", "--install" }, description = "install plugin with locationId")
protected String install;
@Parameter(names = { "-j", "--install-jar" }, description = "install plugin from jar file")
protected String installJar;
@Parameter(names = { "-l", "--list" }, description = "list installed plugins")
protected boolean list;
@Parameter(names = { "-a", "--available" }, description = "list available plugins")
protected boolean available;
@Parameter(names = { "-u", "--update" }, description = "update installed plugins")
protected boolean update;
@Parameter(names = { "--uninstall" }, description = "uninstall plugin with pluginId")
protected String uninstall;
@Parameter(names = { "-h", "--help" }, description = "print this help", help = true)
protected boolean printHelp = false;
@Override
public String name() {
return "plugins";
}
@Override
public void process(JCommanderWrapper<?> jcw, JCommander subCommander) {
if (printHelp) {
jcw.printUsage(subCommander);
return;
}
if (install != null) {
installPlugin(install);
}
if (installJar != null) {
installPlugin("file:" + installJar);
}
if (uninstall != null) {
boolean uninstalled = JadxPluginsTools.getInstance().uninstall(uninstall);
System.out.println(uninstalled ? "Uninstalled" : "Plugin not found");
}
if (update) {
List<JadxPluginUpdate> updates = JadxPluginsTools.getInstance().updateAll();
if (updates.isEmpty()) {
System.out.println("No updates");
} else {
System.out.println("Installed updates: " + updates.size());
for (JadxPluginUpdate update : updates) {
System.out.println(" " + update.getPluginId() + ": " + update.getOldVersion() + " -> " + update.getNewVersion());
}
}
}
if (list) {
List<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();
System.out.println("Installed plugins: " + installed.size());
int i = 1;
for (JadxPluginMetadata plugin : installed) {
System.out.println(" " + (i++) + ") "
+ plugin.getPluginId() + " (" + plugin.getVersion() + ") - "
+ plugin.getName() + ": " + plugin.getDescription());
}
}
if (available) {
List<JadxPluginMetadata> availableList = JadxPluginsList.getInstance().get();
System.out.println("Available plugins: " + availableList.size());
int i = 1;
for (JadxPluginMetadata plugin : availableList) {
System.out.println(" " + (i++) + ") "
+ plugin.getName() + ": " + plugin.getDescription()
+ " (" + plugin.getLocationId() + ")");
}
}
}
private void installPlugin(String locationId) {
JadxPluginMetadata plugin = JadxPluginsTools.getInstance().install(locationId);
System.out.println("Plugin installed: " + plugin.getPluginId() + ":" + plugin.getVersion());
}
}
@@ -0,0 +1,11 @@
package jadx.cli.commands;
import com.beust.jcommander.JCommander;
import jadx.cli.JCommanderWrapper;
public interface ICommand {
String name();
void process(JCommanderWrapper<?> jcw, JCommander subCommander);
}
+5
View File
@@ -1,3 +1,5 @@
<!-- Jadx logger config. Used both in cli and gui -->
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
@@ -5,6 +7,9 @@
</encoder>
</appender>
<!-- jadx-gui -->
<logger name="com.pinterest.ktlint" level="INFO"/>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
@@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test;
import jadx.api.JadxArgs.RenameEnum;
import jadx.cli.JadxCLIArgs.RenameConverter;
import jadx.core.utils.exceptions.JadxArgsValidateException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -38,7 +39,7 @@ public class RenameConverterTest {
@Test
public void wrong() {
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
JadxArgsValidateException thrown = assertThrows(JadxArgsValidateException.class,
() -> converter.convert("wrong"),
"Expected convert() to throw, but it didn't");
@@ -44,6 +44,33 @@ public class TestInput {
decompile("multi", "samples/hello.dex", "samples/HelloWorld.smali");
}
@Test
public void testResourceOnly() throws Exception {
decode("resourceOnly", "samples/resources-only.apk");
}
private void decode(String tmpDirName, String apkSample) throws URISyntaxException, IOException {
List<String> args = new ArrayList<>();
Path tempDir = FileUtils.createTempDir(tmpDirName);
args.add("-v");
args.add("-d");
args.add(tempDir.toAbsolutePath().toString());
URL resource = getClass().getClassLoader().getResource(apkSample);
assertThat(resource).isNotNull();
String sampleFile = resource.toURI().getRawPath();
args.add(sampleFile);
int result = JadxCLI.execute(args.toArray(new String[0]));
assertThat(result).isEqualTo(0);
List<Path> files = Files.find(
tempDir,
3,
(file, attr) -> file.getFileName().toString().equalsIgnoreCase("AndroidManifest.xml"))
.collect(Collectors.toList());
assertThat(files.isEmpty()).isFalse();
}
private void decompile(String tmpDirName, String... inputSamples) throws URISyntaxException, IOException {
List<String> args = new ArrayList<>();
Path tempDir = FileUtils.createTempDir(tmpDirName);
-29
View File
@@ -1,29 +0,0 @@
plugins {
id 'jadx-library'
}
dependencies {
api(project(':jadx-plugins:jadx-plugins-api'))
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'com.android.tools.build:aapt2-proto:4.2.1-7147631'
constraints {
// Force protobuf version to prevent Java-7 issue
implementation 'com.google.protobuf:protobuf-java:3.11.4'
}
testImplementation 'org.apache.commons:commons-lang3:3.12.0'
testImplementation(project(':jadx-plugins:jadx-dex-input'))
testRuntimeOnly(project(':jadx-plugins:jadx-smali-input'))
testRuntimeOnly(project(':jadx-plugins:jadx-java-convert'))
testRuntimeOnly(project(':jadx-plugins:jadx-java-input'))
testRuntimeOnly(project(':jadx-plugins:jadx-raung-input'))
testImplementation 'org.eclipse.jdt:ecj:3.29.0'
testImplementation 'tools.profiler:async-profiler:1.8.3'
}
test {
exclude '**/tmp/*'
}
+37
View File
@@ -0,0 +1,37 @@
plugins {
id("jadx-library")
}
dependencies {
api(project(":jadx-plugins:jadx-input-api"))
implementation("com.google.code.gson:gson:2.10.1")
// TODO: move resources decoding to separate plugin module
implementation("com.android.tools.build:aapt2-proto:8.3.2-10880808")
implementation("com.google.protobuf:protobuf-java") {
version {
require("3.25.3") // version 4 conflict with `aapt2-proto`
}
}
testImplementation("org.apache.commons:commons-lang3:3.14.0")
testImplementation(project(":jadx-plugins:jadx-dex-input"))
testRuntimeOnly(project(":jadx-plugins:jadx-smali-input"))
testRuntimeOnly(project(":jadx-plugins:jadx-java-convert"))
testRuntimeOnly(project(":jadx-plugins:jadx-java-input"))
testRuntimeOnly(project(":jadx-plugins:jadx-raung-input"))
testImplementation("org.eclipse.jdt:ecj") {
version {
prefer("3.33.0")
strictly("[3.33, 3.34[") // from 3.34 compiled with Java 17
}
}
testImplementation("tools.profiler:async-profiler:3.0")
}
tasks.test {
exclude("**/tmp/*")
}
@@ -8,8 +8,6 @@ import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeNodeRef;
public interface ICodeWriter {
String NL = System.getProperty("line.separator");
String INDENT_STR = " ";
boolean isMetadataSupported();
+228 -36
View File
@@ -1,7 +1,8 @@
package jadx.api;
import java.io.Closeable;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
@@ -11,21 +12,39 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.args.DeobfuscationMapFileMode;
import jadx.api.args.GeneratedRenamesMappingFileMode;
import jadx.api.args.IntegerFormat;
import jadx.api.args.ResourceNameSource;
import jadx.api.args.UserRenamesMappingsMode;
import jadx.api.data.ICodeData;
import jadx.api.deobf.IAliasProvider;
import jadx.api.deobf.IRenameCondition;
import jadx.api.impl.AnnotatedCodeWriter;
import jadx.api.impl.InMemoryCodeCache;
import jadx.api.plugins.loader.JadxBasePluginLoader;
import jadx.api.plugins.loader.JadxPluginLoader;
import jadx.api.usage.IUsageInfoCache;
import jadx.api.usage.impl.InMemoryUsageInfoCache;
import jadx.core.deobf.DeobfAliasProvider;
import jadx.core.deobf.conditions.DeobfWhitelist;
import jadx.core.deobf.conditions.JadxRenameConditions;
import jadx.core.plugins.PluginContext;
import jadx.core.utils.files.FileUtils;
public class JadxArgs {
public class JadxArgs implements Closeable {
private static final Logger LOG = LoggerFactory.getLogger(JadxArgs.class);
public static final int DEFAULT_THREADS_COUNT = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
public static final String DEFAULT_NEW_LINE_STR = System.lineSeparator();
public static final String DEFAULT_INDENT_STR = " ";
public static final String DEFAULT_OUT_DIR = "jadx-output";
public static final String DEFAULT_SRC_DIR = "sources";
public static final String DEFAULT_RES_DIR = "resources";
@@ -37,6 +56,13 @@ public class JadxArgs {
private File outDirRes;
private ICodeCache codeCache = new InMemoryCodeCache();
/**
* Usage data cache. Saves use places of classes, methods and fields between code reloads.
* Can be set to {@link jadx.api.usage.impl.EmptyUsageInfoCache} if code reload not needed.
*/
private IUsageInfoCache usageInfoCache = new InMemoryUsageInfoCache();
private Function<JadxArgs, ICodeWriter> codeWriterProvider = AnnotatedCodeWriter::new;
private int threadsCount = DEFAULT_THREADS_COUNT;
@@ -52,6 +78,8 @@ public class JadxArgs {
private boolean extractFinally = true;
private boolean inlineAnonymousClasses = true;
private boolean inlineMethods = true;
private boolean allowInlineKotlinLambda = true;
private boolean moveInnerClasses = true;
private boolean skipResources = false;
private boolean skipSources = false;
@@ -66,21 +94,41 @@ public class JadxArgs {
*/
private boolean includeDependencies = false;
private Path userRenamesMappingsPath = null;
private UserRenamesMappingsMode userRenamesMappingsMode = UserRenamesMappingsMode.getDefault();
private boolean deobfuscationOn = false;
private boolean useSourceNameAsClassAlias = false;
private boolean parseKotlinMetadata = false;
private File deobfuscationMapFile = null;
private DeobfuscationMapFileMode deobfuscationMapFileMode = DeobfuscationMapFileMode.READ;
private File generatedRenamesMappingFile = null;
private GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.getDefault();
private ResourceNameSource resourceNameSource = ResourceNameSource.AUTO;
private int deobfuscationMinLength = 0;
private int deobfuscationMaxLength = Integer.MAX_VALUE;
/**
* List of classes and packages (ends with '.*') to exclude from deobfuscation
*/
private List<String> deobfuscationWhitelist = DeobfWhitelist.DEFAULT_LIST;
/**
* Nodes alias provider for deobfuscator and rename visitor
*/
private IAliasProvider aliasProvider = new DeobfAliasProvider();
/**
* Condition to rename node in deobfuscator
*/
private IRenameCondition renameCondition = JadxRenameConditions.buildDefault();
private boolean escapeUnicode = false;
private boolean replaceConsts = true;
private boolean respectBytecodeAccModifiers = false;
private boolean exportAsGradleProject = false;
private boolean skipXmlPrettyPrint = false;
private boolean fsCaseSensitive;
public enum RenameEnum {
@@ -99,8 +147,14 @@ public class JadxArgs {
private ICodeData codeData;
private String codeNewLineStr = DEFAULT_NEW_LINE_STR;
private String codeIndentStr = DEFAULT_INDENT_STR;
private CommentsLevel commentsLevel = CommentsLevel.INFO;
private IntegerFormat integerFormat = IntegerFormat.AUTO;
private boolean useDxInput = false;
public enum UseKotlinMethodsForVarNames {
@@ -116,6 +170,10 @@ public class JadxArgs {
private Map<String, String> pluginOptions = new HashMap<>();
private JadxPluginLoader pluginLoader = new JadxBasePluginLoader();
private boolean loadJadxClsSetFile = true;
public JadxArgs() {
// use default options
}
@@ -126,16 +184,24 @@ public class JadxArgs {
setOutDirRes(new File(rootDir, DEFAULT_RES_DIR));
}
@Override
public void close() {
try {
inputFiles.clear();
inputFiles = null;
if (codeCache != null) {
codeCache.close();
}
if (usageInfoCache != null) {
usageInfoCache.close();
}
if (pluginLoader != null) {
pluginLoader.close();
}
} catch (Exception e) {
LOG.error("Failed to close JadxArgs", e);
} finally {
codeCache = null;
usageInfoCache = null;
}
}
@@ -261,6 +327,22 @@ public class JadxArgs {
this.inlineMethods = inlineMethods;
}
public boolean isAllowInlineKotlinLambda() {
return allowInlineKotlinLambda;
}
public void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) {
this.allowInlineKotlinLambda = allowInlineKotlinLambda;
}
public boolean isMoveInnerClasses() {
return moveInnerClasses;
}
public void setMoveInnerClasses(boolean moveInnerClasses) {
this.moveInnerClasses = moveInnerClasses;
}
public boolean isExtractFinally() {
return extractFinally;
}
@@ -301,6 +383,22 @@ public class JadxArgs {
this.classFilter = classFilter;
}
public Path getUserRenamesMappingsPath() {
return userRenamesMappingsPath;
}
public void setUserRenamesMappingsPath(Path path) {
this.userRenamesMappingsPath = path;
}
public UserRenamesMappingsMode getUserRenamesMappingsMode() {
return userRenamesMappingsMode;
}
public void setUserRenamesMappingsMode(UserRenamesMappingsMode mode) {
this.userRenamesMappingsMode = mode;
}
public boolean isDeobfuscationOn() {
return deobfuscationOn;
}
@@ -309,24 +407,22 @@ public class JadxArgs {
this.deobfuscationOn = deobfuscationOn;
}
@Deprecated
public boolean isDeobfuscationForceSave() {
return deobfuscationMapFileMode == DeobfuscationMapFileMode.OVERWRITE;
return generatedRenamesMappingFileMode == GeneratedRenamesMappingFileMode.OVERWRITE;
}
@Deprecated
public void setDeobfuscationForceSave(boolean deobfuscationForceSave) {
if (deobfuscationForceSave) {
this.deobfuscationMapFileMode = DeobfuscationMapFileMode.OVERWRITE;
this.generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.OVERWRITE;
}
}
public DeobfuscationMapFileMode getDeobfuscationMapFileMode() {
return deobfuscationMapFileMode;
public GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {
return generatedRenamesMappingFileMode;
}
public void setDeobfuscationMapFileMode(DeobfuscationMapFileMode deobfuscationMapFileMode) {
this.deobfuscationMapFileMode = deobfuscationMapFileMode;
public void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode mode) {
this.generatedRenamesMappingFileMode = mode;
}
public boolean isUseSourceNameAsClassAlias() {
@@ -337,14 +433,6 @@ public class JadxArgs {
this.useSourceNameAsClassAlias = useSourceNameAsClassAlias;
}
public boolean isParseKotlinMetadata() {
return parseKotlinMetadata;
}
public void setParseKotlinMetadata(boolean parseKotlinMetadata) {
this.parseKotlinMetadata = parseKotlinMetadata;
}
public int getDeobfuscationMinLength() {
return deobfuscationMinLength;
}
@@ -361,12 +449,44 @@ public class JadxArgs {
this.deobfuscationMaxLength = deobfuscationMaxLength;
}
public File getDeobfuscationMapFile() {
return deobfuscationMapFile;
public List<String> getDeobfuscationWhitelist() {
return this.deobfuscationWhitelist;
}
public void setDeobfuscationMapFile(File deobfuscationMapFile) {
this.deobfuscationMapFile = deobfuscationMapFile;
public void setDeobfuscationWhitelist(List<String> deobfuscationWhitelist) {
this.deobfuscationWhitelist = deobfuscationWhitelist;
}
public File getGeneratedRenamesMappingFile() {
return generatedRenamesMappingFile;
}
public void setGeneratedRenamesMappingFile(File file) {
this.generatedRenamesMappingFile = file;
}
public ResourceNameSource getResourceNameSource() {
return resourceNameSource;
}
public void setResourceNameSource(ResourceNameSource resourceNameSource) {
this.resourceNameSource = resourceNameSource;
}
public IAliasProvider getAliasProvider() {
return aliasProvider;
}
public void setAliasProvider(IAliasProvider aliasProvider) {
this.aliasProvider = aliasProvider;
}
public IRenameCondition getRenameCondition() {
return renameCondition;
}
public void setRenameCondition(IRenameCondition renameCondition) {
this.renameCondition = renameCondition;
}
public boolean isEscapeUnicode() {
@@ -401,6 +521,14 @@ public class JadxArgs {
this.exportAsGradleProject = exportAsGradleProject;
}
public boolean isSkipXmlPrettyPrint() {
return skipXmlPrettyPrint;
}
public void setSkipXmlPrettyPrint(boolean skipXmlPrettyPrint) {
this.skipXmlPrettyPrint = skipXmlPrettyPrint;
}
public boolean isFsCaseSensitive() {
return fsCaseSensitive;
}
@@ -485,6 +613,14 @@ public class JadxArgs {
this.codeWriterProvider = codeWriterProvider;
}
public IUsageInfoCache getUsageInfoCache() {
return usageInfoCache;
}
public void setUsageInfoCache(IUsageInfoCache usageInfoCache) {
this.usageInfoCache = usageInfoCache;
}
public ICodeData getCodeData() {
return codeData;
}
@@ -493,6 +629,22 @@ public class JadxArgs {
this.codeData = codeData;
}
public String getCodeIndentStr() {
return codeIndentStr;
}
public void setCodeIndentStr(String codeIndentStr) {
this.codeIndentStr = codeIndentStr;
}
public String getCodeNewLineStr() {
return codeNewLineStr;
}
public void setCodeNewLineStr(String codeNewLineStr) {
this.codeNewLineStr = codeNewLineStr;
}
public CommentsLevel getCommentsLevel() {
return commentsLevel;
}
@@ -501,6 +653,14 @@ public class JadxArgs {
this.commentsLevel = commentsLevel;
}
public IntegerFormat getIntegerFormat() {
return integerFormat;
}
public void setIntegerFormat(IntegerFormat format) {
this.integerFormat = format;
}
public boolean isUseDxInput() {
return useDxInput;
}
@@ -533,19 +693,47 @@ public class JadxArgs {
this.pluginOptions = pluginOptions;
}
public JadxPluginLoader getPluginLoader() {
return pluginLoader;
}
public void setPluginLoader(JadxPluginLoader pluginLoader) {
this.pluginLoader = pluginLoader;
}
public boolean isLoadJadxClsSetFile() {
return loadJadxClsSetFile;
}
public void setLoadJadxClsSetFile(boolean loadJadxClsSetFile) {
this.loadJadxClsSetFile = loadJadxClsSetFile;
}
/**
* Hash of all options that can change result code
*/
public String makeCodeArgsHash() {
public String makeCodeArgsHash(@Nullable JadxDecompiler decompiler) {
String argStr = "args:" + decompilationMode + useImports + showInconsistentCode
+ inlineAnonymousClasses + inlineMethods
+ deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength
+ parseKotlinMetadata + useKotlinMethodsForVarNames
+ inlineAnonymousClasses + inlineMethods + moveInnerClasses + allowInlineKotlinLambda
+ deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength + deobfuscationWhitelist
+ resourceNameSource
+ useKotlinMethodsForVarNames
+ insertDebugLines + extractFinally
+ debugInfo + useSourceNameAsClassAlias + escapeUnicode + replaceConsts
+ respectBytecodeAccModifiers + fsCaseSensitive + renameFlags
+ commentsLevel + useDxInput + pluginOptions;
return FileUtils.md5Sum(argStr.getBytes(StandardCharsets.US_ASCII));
+ commentsLevel + useDxInput + integerFormat
+ "|" + buildPluginsHash(decompiler);
return FileUtils.md5Sum(argStr);
}
private static String buildPluginsHash(@Nullable JadxDecompiler decompiler) {
if (decompiler == null) {
return "";
}
return decompiler.getPluginManager().getResolvedPluginContexts()
.stream()
.map(PluginContext::getInputsHash)
.collect(Collectors.joining(":"));
}
@Override
@@ -561,20 +749,24 @@ public class JadxArgs {
+ ", skipResources=" + skipResources
+ ", skipSources=" + skipSources
+ ", includeDependencies=" + includeDependencies
+ ", userRenamesMappingsPath=" + userRenamesMappingsPath
+ ", userRenamesMappingsMode=" + userRenamesMappingsMode
+ ", deobfuscationOn=" + deobfuscationOn
+ ", deobfuscationMapFile=" + deobfuscationMapFile
+ ", deobfuscationMapFileMode=" + deobfuscationMapFileMode
+ ", generatedRenamesMappingFile=" + generatedRenamesMappingFile
+ ", generatedRenamesMappingFileMode=" + generatedRenamesMappingFileMode
+ ", resourceNameSource=" + resourceNameSource
+ ", useSourceNameAsClassAlias=" + useSourceNameAsClassAlias
+ ", parseKotlinMetadata=" + parseKotlinMetadata
+ ", useKotlinMethodsForVarNames=" + useKotlinMethodsForVarNames
+ ", insertDebugLines=" + insertDebugLines
+ ", extractFinally=" + extractFinally
+ ", deobfuscationMinLength=" + deobfuscationMinLength
+ ", deobfuscationMaxLength=" + deobfuscationMaxLength
+ ", deobfuscationWhitelist=" + deobfuscationWhitelist
+ ", escapeUnicode=" + escapeUnicode
+ ", replaceConsts=" + replaceConsts
+ ", respectBytecodeAccModifiers=" + respectBytecodeAccModifiers
+ ", exportAsGradleProject=" + exportAsGradleProject
+ ", skipXmlPrettyPrint=" + skipXmlPrettyPrint
+ ", fsCaseSensitive=" + fsCaseSensitive
+ ", renameFlags=" + renameFlags
+ ", outputFormat=" + outputFormat
@@ -25,15 +25,9 @@ public class JadxArgsValidator {
private static void checkInputFiles(JadxDecompiler jadx, JadxArgs args) {
List<File> inputFiles = args.getInputFiles();
if (inputFiles.isEmpty() && jadx.getCustomLoads().isEmpty()) {
if (inputFiles.isEmpty() && jadx.getCustomCodeLoaders().isEmpty()) {
throw new JadxArgsValidateException("Please specify input file");
}
for (File inputFile : inputFiles) {
String fileName = inputFile.getName();
if (fileName.startsWith("--")) {
throw new JadxArgsValidateException("Unknown argument: " + fileName);
}
}
for (File file : inputFiles) {
checkFile(file);
}
@@ -6,17 +6,12 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -30,28 +25,34 @@ import jadx.api.metadata.ICodeNodeRef;
import jadx.api.metadata.annotations.NodeDeclareRef;
import jadx.api.metadata.annotations.VarNode;
import jadx.api.metadata.annotations.VarRef;
import jadx.api.plugins.CustomResourcesLoader;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.JadxPluginManager;
import jadx.api.plugins.input.JadxInputPlugin;
import jadx.api.plugins.input.data.ILoadResult;
import jadx.api.plugins.options.JadxPluginOptions;
import jadx.api.plugins.events.IJadxEvents;
import jadx.api.plugins.input.ICodeLoader;
import jadx.api.plugins.input.JadxCodeInput;
import jadx.api.plugins.pass.JadxPass;
import jadx.api.plugins.pass.types.JadxAfterLoadPass;
import jadx.api.plugins.pass.types.JadxPassType;
import jadx.api.utils.tasks.ITaskExecutor;
import jadx.core.Jadx;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.InlinedAttr;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.PackageNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.SaveCode;
import jadx.core.export.ExportGradleProject;
import jadx.core.export.ExportGradleTask;
import jadx.core.plugins.JadxPluginManager;
import jadx.core.plugins.PluginContext;
import jadx.core.plugins.events.JadxEventsImpl;
import jadx.core.utils.DecompilerScheduler;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.core.utils.tasks.TaskExecutor;
import jadx.core.xmlgen.BinaryXMLParser;
import jadx.core.xmlgen.ProtoXMLParser;
import jadx.core.xmlgen.ResContainer;
import jadx.core.xmlgen.ResourcesSaver;
/**
@@ -85,8 +86,8 @@ public final class JadxDecompiler implements Closeable {
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
private final JadxArgs args;
private final JadxPluginManager pluginManager = new JadxPluginManager();
private final List<ILoadResult> loadedInputs = new ArrayList<>();
private final JadxPluginManager pluginManager = new JadxPluginManager(this);
private final List<ICodeLoader> loadedInputs = new ArrayList<>();
private RootNode root;
private List<JavaClass> classes;
@@ -95,13 +96,12 @@ public final class JadxDecompiler implements Closeable {
private BinaryXMLParser binaryXmlParser;
private ProtoXMLParser protoXmlParser;
private final Map<ClassNode, JavaClass> classesMap = new ConcurrentHashMap<>();
private final Map<MethodNode, JavaMethod> methodsMap = new ConcurrentHashMap<>();
private final Map<FieldNode, JavaField> fieldsMap = new ConcurrentHashMap<>();
private final IDecompileScheduler decompileScheduler = new DecompilerScheduler();
private final JadxEventsImpl events = new JadxEventsImpl();
private final List<ILoadResult> customLoads = new ArrayList<>();
private final List<ICodeLoader> customCodeLoaders = new ArrayList<>();
private final List<CustomResourcesLoader> customResourcesLoaders = new ArrayList<>();
private final Map<JadxPassType, List<JadxPass>> customPasses = new HashMap<>();
public JadxDecompiler() {
this(new JadxArgs());
@@ -115,15 +115,31 @@ public final class JadxDecompiler implements Closeable {
reset();
JadxArgsValidator.validate(this);
LOG.info("loading ...");
loadPlugins(args);
loadPlugins();
loadInputFiles();
root = new RootNode(args);
root.init();
root.setDecompilerRef(this);
root.mergePasses(customPasses);
root.loadClasses(loadedInputs);
root.initClassPath();
root.loadResources(getResources());
root.runPreDecompileStage();
root.initPasses();
loadFinished();
}
public void reloadPasses() {
LOG.info("reloading (passes only) ...");
customPasses.clear();
root.resetPasses();
events.reset();
loadPlugins();
root.mergePasses(customPasses);
root.restartVisitors();
root.initPasses();
loadFinished();
}
private void loadInputFiles() {
@@ -131,42 +147,38 @@ public final class JadxDecompiler implements Closeable {
List<Path> inputPaths = Utils.collectionMap(args.getInputFiles(), File::toPath);
List<Path> inputFiles = FileUtils.expandDirs(inputPaths);
long start = System.currentTimeMillis();
for (JadxInputPlugin inputPlugin : pluginManager.getInputPlugins()) {
ILoadResult loadResult = inputPlugin.loadFiles(inputFiles);
if (loadResult != null && !loadResult.isEmpty()) {
loadedInputs.add(loadResult);
for (PluginContext plugin : pluginManager.getResolvedPluginContexts()) {
for (JadxCodeInput codeLoader : plugin.getCodeInputs()) {
try {
ICodeLoader loader = codeLoader.loadFiles(inputFiles);
if (loader != null && !loader.isEmpty()) {
loadedInputs.add(loader);
}
} catch (Exception e) {
throw new JadxRuntimeException("Failed to load code for plugin: " + plugin, e);
}
}
}
loadedInputs.addAll(customLoads);
loadedInputs.addAll(customCodeLoaders);
if (LOG.isDebugEnabled()) {
LOG.debug("Loaded using {} inputs plugin in {} ms", loadedInputs.size(), System.currentTimeMillis() - start);
}
}
public void addCustomLoad(ILoadResult customLoad) {
customLoads.add(customLoad);
}
public List<ILoadResult> getCustomLoads() {
return customLoads;
}
private void reset() {
root = null;
classes = null;
resources = null;
binaryXmlParser = null;
protoXmlParser = null;
classesMap.clear();
methodsMap.clear();
fieldsMap.clear();
events.reset();
}
@Override
public void close() {
reset();
closeInputs();
closeLoaders();
args.close();
}
@@ -181,23 +193,37 @@ public final class JadxDecompiler implements Closeable {
loadedInputs.clear();
}
private void loadPlugins(JadxArgs args) {
pluginManager.providesSuggestion("java-input", args.isUseDxInput() ? "java-convert" : "java-input");
pluginManager.load();
if (LOG.isDebugEnabled()) {
LOG.debug("Resolved plugins: {}", Utils.collectionMap(pluginManager.getResolvedPlugins(),
p -> p.getPluginInfo().getPluginId()));
private void closeLoaders() {
for (CustomResourcesLoader resourcesLoader : customResourcesLoaders) {
try {
resourcesLoader.close();
} catch (Exception e) {
LOG.error("Failed to close resource loader: " + resourcesLoader, e);
}
}
Map<String, String> pluginOptions = args.getPluginOptions();
if (!pluginOptions.isEmpty()) {
LOG.debug("Applying plugin options: {}", pluginOptions);
for (JadxPluginOptions plugin : pluginManager.getPluginsWithOptions()) {
try {
plugin.setOptions(pluginOptions);
} catch (Exception e) {
String pluginId = plugin.getPluginInfo().getPluginId();
throw new JadxRuntimeException("Failed to apply options for plugin: " + pluginId, e);
}
customResourcesLoaders.clear();
}
private void loadPlugins() {
pluginManager.providesSuggestion("java-input", args.isUseDxInput() ? "java-convert" : "java-input");
pluginManager.load(args.getPluginLoader());
if (LOG.isDebugEnabled()) {
LOG.debug("Resolved plugins: {}", pluginManager.getResolvedPluginContexts());
}
pluginManager.initResolved();
if (LOG.isDebugEnabled()) {
List<String> passes = customPasses.values().stream().flatMap(Collection::stream)
.map(p -> p.getInfo().getName()).collect(Collectors.toList());
LOG.debug("Loaded custom passes: {} {}", passes.size(), passes);
}
}
private void loadFinished() {
LOG.debug("Load finished");
List<JadxPass> list = customPasses.get(JadxAfterLoadPass.TYPE);
if (list != null) {
for (JadxPass pass : list) {
((JadxAfterLoadPass) pass).init(this);
}
}
}
@@ -221,13 +247,12 @@ public final class JadxDecompiler implements Closeable {
@SuppressWarnings("BusyWait")
public void save(int intervalInMillis, ProgressListener listener) {
ThreadPoolExecutor ex = (ThreadPoolExecutor) getSaveExecutor();
ex.shutdown();
try {
long total = ex.getTaskCount();
while (ex.isTerminating()) {
long done = ex.getCompletedTaskCount();
listener.progress(done, total);
ITaskExecutor tasks = getSaveTaskExecutor();
tasks.execute();
long total = tasks.getTasksCount();
while (tasks.isRunning()) {
listener.progress(tasks.getProgress(), total);
Thread.sleep(intervalInMillis);
}
} catch (InterruptedException e) {
@@ -244,97 +269,122 @@ public final class JadxDecompiler implements Closeable {
save(false, true);
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private void save(boolean saveSources, boolean saveResources) {
ExecutorService ex = getSaveExecutor(saveSources, saveResources);
ex.shutdown();
try {
ex.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
LOG.error("Save interrupted", e);
Thread.currentThread().interrupt();
}
ITaskExecutor executor = getSaveTasks(saveSources, saveResources);
executor.execute();
executor.awaitTermination();
}
public ExecutorService getSaveExecutor() {
return getSaveExecutor(!args.isSkipSources(), !args.isSkipResources());
}
public List<Runnable> getSaveTasks() {
public ITaskExecutor getSaveTaskExecutor() {
return getSaveTasks(!args.isSkipSources(), !args.isSkipResources());
}
private ExecutorService getSaveExecutor(boolean saveSources, boolean saveResources) {
int threadsCount = args.getThreadsCount();
LOG.debug("processing threads count: {}", threadsCount);
LOG.info("processing ...");
ExecutorService executor = Executors.newFixedThreadPool(threadsCount);
List<Runnable> tasks = getSaveTasks(saveSources, saveResources);
tasks.forEach(executor::execute);
return executor;
@Deprecated(forRemoval = true)
public ExecutorService getSaveExecutor() {
ITaskExecutor executor = getSaveTaskExecutor();
executor.execute();
return executor.getInternalExecutor();
}
private List<Runnable> getSaveTasks(boolean saveSources, boolean saveResources) {
@Deprecated(forRemoval = true)
public List<Runnable> getSaveTasks() {
return Collections.singletonList(this::save);
}
private TaskExecutor getSaveTasks(boolean saveSources, boolean saveResources) {
if (root == null) {
throw new JadxRuntimeException("No loaded files");
}
File sourcesOutDir;
File resOutDir;
ExportGradleTask gradleExportTask;
if (args.isExportAsGradleProject()) {
ResourceFile androidManifest = resources.stream()
.filter(resourceFile -> resourceFile.getType() == ResourceType.MANIFEST)
.findFirst()
.orElseThrow(IllegalStateException::new);
ResContainer strings = resources.stream()
.filter(resourceFile -> resourceFile.getType() == ResourceType.ARSC)
.findFirst()
.orElseThrow(IllegalStateException::new)
.loadContent()
.getSubFiles()
.stream()
.filter(resContainer -> resContainer.getFileName().contains("strings.xml"))
.findFirst()
.orElseThrow(IllegalStateException::new);
ExportGradleProject export = new ExportGradleProject(root, args.getOutDir(), androidManifest, strings);
export.init();
sourcesOutDir = export.getSrcOutDir();
resOutDir = export.getResOutDir();
gradleExportTask = new ExportGradleTask(resources, root, args.getOutDir());
gradleExportTask.init();
sourcesOutDir = gradleExportTask.getSrcOutDir();
resOutDir = gradleExportTask.getResOutDir();
} else {
sourcesOutDir = args.getOutDirSrc();
resOutDir = args.getOutDirRes();
gradleExportTask = null;
}
List<Runnable> tasks = new ArrayList<>();
// save resources first because decompilation can hang or fail
TaskExecutor executor = new TaskExecutor();
executor.setThreadsCount(args.getThreadsCount());
if (saveResources) {
appendResourcesSaveTasks(tasks, resOutDir);
// save resources first because decompilation can stop or fail
appendResourcesSaveTasks(executor, resOutDir);
}
if (saveSources) {
appendSourcesSave(tasks, sourcesOutDir);
appendSourcesSave(executor, sourcesOutDir);
}
return tasks;
if (gradleExportTask != null) {
executor.addSequentialTask(gradleExportTask);
}
return executor;
}
private void appendResourcesSaveTasks(List<Runnable> tasks, File outDir) {
private void appendResourcesSaveTasks(ITaskExecutor executor, File outDir) {
if (args.isSkipFilesSave()) {
return;
}
Set<String> inputFileNames = args.getInputFiles().stream().map(File::getAbsolutePath).collect(Collectors.toSet());
// process AndroidManifest.xml first to load complete resource ids table
for (ResourceFile resourceFile : getResources()) {
if (resourceFile.getType() != ResourceType.ARSC
if (resourceFile.getType() == ResourceType.MANIFEST) {
new ResourcesSaver(outDir, resourceFile).run();
break;
}
}
Set<String> inputFileNames = args.getInputFiles().stream()
.map(File::getAbsolutePath)
.collect(Collectors.toSet());
List<Runnable> tasks = new ArrayList<>();
for (ResourceFile resourceFile : getResources()) {
ResourceType resType = resourceFile.getType();
if (resType == ResourceType.MANIFEST) {
// already processed
continue;
}
if (resType != ResourceType.ARSC
&& inputFileNames.contains(resourceFile.getOriginalName())) {
// ignore resource made from input file
continue;
}
tasks.add(new ResourcesSaver(outDir, resourceFile));
}
executor.addParallelTasks(tasks);
}
private void appendSourcesSave(List<Runnable> tasks, File outDir) {
Predicate<String> classFilter = args.getClassFilter();
private void appendSourcesSave(ITaskExecutor executor, File outDir) {
List<JavaClass> classes = getClasses();
List<JavaClass> processQueue = new ArrayList<>(classes.size());
List<JavaClass> processQueue = filterClasses(classes);
List<List<JavaClass>> batches;
try {
batches = decompileScheduler.buildBatches(processQueue);
} catch (Exception e) {
throw new JadxRuntimeException("Decompilation batches build failed", e);
}
List<Runnable> decompileTasks = new ArrayList<>(batches.size());
for (List<JavaClass> decompileBatch : batches) {
decompileTasks.add(() -> {
for (JavaClass cls : decompileBatch) {
try {
ClassNode clsNode = cls.getClassNode();
ICodeInfo code = clsNode.getCode();
SaveCode.save(outDir, clsNode, code);
} catch (Exception e) {
LOG.error("Error saving class: {}", cls, e);
}
}
});
}
executor.addParallelTasks(decompileTasks);
}
private List<JavaClass> filterClasses(List<JavaClass> classes) {
Predicate<String> classFilter = args.getClassFilter();
List<JavaClass> list = new ArrayList<>(classes.size());
for (JavaClass cls : classes) {
ClassNode clsNode = cls.getClassNode();
if (clsNode.contains(AFlag.DONT_GENERATE)) {
@@ -346,26 +396,9 @@ public final class JadxDecompiler implements Closeable {
}
continue;
}
processQueue.add(cls);
}
List<List<JavaClass>> batches;
try {
batches = decompileScheduler.buildBatches(processQueue);
} catch (Exception e) {
throw new JadxRuntimeException("Decompilation batches build failed", e);
}
for (List<JavaClass> decompileBatch : batches) {
tasks.add(() -> {
for (JavaClass cls : decompileBatch) {
try {
ICodeInfo code = cls.getCodeInfo();
SaveCode.save(outDir, cls.getClassNode(), code);
} catch (Exception e) {
LOG.error("Error saving class: {}", cls, e);
}
}
});
list.add(cls);
}
return list;
}
public List<JavaClass> getClasses() {
@@ -392,7 +425,7 @@ public final class JadxDecompiler implements Closeable {
return Utils.collectionMap(root.getClasses(), this::convertClassNode);
}
public List<ResourceFile> getResources() {
public synchronized List<ResourceFile> getResources() {
if (resources == null) {
if (root == null) {
return Collections.emptyList();
@@ -403,25 +436,7 @@ public final class JadxDecompiler implements Closeable {
}
public List<JavaPackage> getPackages() {
List<JavaClass> classList = getClasses();
if (classList.isEmpty()) {
return Collections.emptyList();
}
Map<String, List<JavaClass>> map = new HashMap<>();
for (JavaClass javaClass : classList) {
String pkg = javaClass.getPackage();
List<JavaClass> clsList = map.computeIfAbsent(pkg, k -> new ArrayList<>());
clsList.add(javaClass);
}
List<JavaPackage> packages = new ArrayList<>(map.size());
for (Map.Entry<String, List<JavaClass>> entry : map.entrySet()) {
packages.add(new JavaPackage(entry.getKey(), entry.getValue()));
}
Collections.sort(packages);
for (JavaPackage pkg : packages) {
pkg.getClasses().sort(Comparator.comparing(JavaClass::getName, String.CASE_INSENSITIVE_ORDER));
}
return Collections.unmodifiableList(packages);
return Utils.collectionMap(root.getPackages(), this::convertPackageNode);
}
public int getErrorsCount() {
@@ -472,47 +487,56 @@ public final class JadxDecompiler implements Closeable {
* Get JavaClass by ClassNode without loading and decompilation
*/
@ApiStatus.Internal
JavaClass convertClassNode(ClassNode cls) {
return classesMap.compute(cls, (node, prevJavaCls) -> {
if (prevJavaCls != null && prevJavaCls.getClassNode() == cls) {
// keep previous variable
return prevJavaCls;
}
if (cls.isInner()) {
return new JavaClass(cls, convertClassNode(cls.getParentClass()));
}
return new JavaClass(cls, this);
});
synchronized JavaClass convertClassNode(ClassNode cls) {
JavaClass javaClass = cls.getJavaNode();
if (javaClass == null) {
javaClass = cls.isInner()
? new JavaClass(cls, convertClassNode(cls.getParentClass()))
: new JavaClass(cls, this);
cls.setJavaNode(javaClass);
}
return javaClass;
}
@ApiStatus.Internal
JavaField convertFieldNode(FieldNode field) {
return fieldsMap.computeIfAbsent(field, fldNode -> {
JavaClass parentCls = convertClassNode(fldNode.getParentClass());
return new JavaField(parentCls, fldNode);
});
synchronized JavaField convertFieldNode(FieldNode fld) {
JavaField javaField = fld.getJavaNode();
if (javaField == null) {
JavaClass parentCls = convertClassNode(fld.getParentClass());
javaField = new JavaField(parentCls, fld);
fld.setJavaNode(javaField);
}
return javaField;
}
@ApiStatus.Internal
JavaMethod convertMethodNode(MethodNode method) {
return methodsMap.computeIfAbsent(method, mthNode -> {
ClassNode codeCls = getCodeParentClass(mthNode.getParentClass());
return new JavaMethod(convertClassNode(codeCls), mthNode);
});
synchronized JavaMethod convertMethodNode(MethodNode mth) {
JavaMethod javaMethod = mth.getJavaNode();
if (javaMethod == null) {
javaMethod = new JavaMethod(convertClassNode(mth.getParentClass()), mth);
mth.setJavaNode(javaMethod);
}
return javaMethod;
}
private static ClassNode getCodeParentClass(ClassNode cls) {
ClassNode codeCls;
InlinedAttr inlinedAttr = cls.get(AType.INLINED);
if (inlinedAttr != null) {
codeCls = inlinedAttr.getInlineCls().getTopParentClass();
} else {
codeCls = cls.getTopParentClass();
@ApiStatus.Internal
synchronized JavaPackage convertPackageNode(PackageNode pkg) {
JavaPackage foundPkg = pkg.getJavaNode();
if (foundPkg != null) {
return foundPkg;
}
if (codeCls == cls) {
return codeCls;
List<JavaClass> clsList = Utils.collectionMap(pkg.getClasses(), this::convertClassNode);
int subPkgsCount = pkg.getSubPackages().size();
List<JavaPackage> subPkgs = subPkgsCount == 0 ? Collections.emptyList() : new ArrayList<>(subPkgsCount);
JavaPackage javaPkg = new JavaPackage(pkg, clsList, subPkgs);
if (subPkgsCount != 0) {
// add subpackages after parent to avoid endless recursion
for (PackageNode subPackage : pkg.getSubPackages()) {
subPkgs.add(convertPackageNode(subPackage));
}
}
return getCodeParentClass(codeCls);
pkg.setJavaNode(javaPkg);
return javaPkg;
}
@Nullable
@@ -589,14 +613,9 @@ public final class JadxDecompiler implements Closeable {
}
}
@Nullable
private JavaVariable resolveVarNode(VarNode varNode) {
MethodNode mthNode = varNode.getMth();
JavaMethod mth = convertMethodNode(mthNode);
if (mth == null) {
return null;
}
return new JavaVariable(mth, varNode);
JavaMethod javaNode = convertMethodNode(varNode.getMth());
return new JavaVariable(javaNode, varNode);
}
@Nullable
@@ -658,6 +677,33 @@ public final class JadxDecompiler implements Closeable {
return decompileScheduler;
}
public IJadxEvents events() {
return events;
}
public void addCustomCodeLoader(ICodeLoader customCodeLoader) {
customCodeLoaders.add(customCodeLoader);
}
public List<ICodeLoader> getCustomCodeLoaders() {
return customCodeLoaders;
}
public void addCustomResourcesLoader(CustomResourcesLoader loader) {
if (customResourcesLoaders.contains(loader)) {
return;
}
customResourcesLoaders.add(loader);
}
public List<CustomResourcesLoader> getCustomResourcesLoaders() {
return customResourcesLoaders;
}
public void addCustomPass(JadxPass pass) {
customPasses.computeIfAbsent(pass.getPassType(), l -> new ArrayList<>()).add(pass);
}
@Override
public String toString() {
return "jadx decompiler " + getVersion();
+53 -20
View File
@@ -18,6 +18,7 @@ import jadx.api.metadata.ICodeNodeRef;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.AnonymousClassAttr;
import jadx.core.dex.attributes.nodes.InlinedAttr;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
@@ -57,7 +58,10 @@ public final class JavaClass implements JavaNode {
}
public @NotNull ICodeInfo getCodeInfo() {
load();
ICodeInfo code = load();
if (code != null) {
return code;
}
return cls.decompile();
}
@@ -95,6 +99,11 @@ public final class JavaClass implements JavaNode {
return false;
}
@Override
public ICodeNodeRef getCodeNodeRef() {
return cls;
}
/**
* Internal API. Not Stable!
*/
@@ -106,19 +115,24 @@ public final class JavaClass implements JavaNode {
/**
* Decompile class and loads internal lists of fields, methods, etc.
* Do nothing if already loaded.
*
* @return code info if decompilation was executed, null otherwise
*/
@Nullable
private synchronized void load() {
private synchronized @Nullable ICodeInfo load() {
if (listsLoaded) {
return;
return null;
}
listsLoaded = true;
JadxDecompiler rootDecompiler = getRootDecompiler();
ICodeCache codeCache = rootDecompiler.getArgs().getCodeCache();
if (!codeCache.contains(cls.getRawName())) {
cls.decompile();
ICodeInfo code;
if (cls.getState().isProcessComplete()) {
// already decompiled -> class internals loaded
code = null;
} else {
code = cls.decompile();
}
JadxDecompiler rootDecompiler = getRootDecompiler();
int inClsCount = cls.getInnerClasses().size();
if (inClsCount != 0) {
List<JavaClass> list = new ArrayList<>(inClsCount);
@@ -164,6 +178,7 @@ public final class JavaClass implements JavaNode {
mths.sort(Comparator.comparing(JavaMethod::getName));
this.methods = Collections.unmodifiableList(mths);
}
return code;
}
JadxDecompiler getRootDecompiler() {
@@ -242,19 +257,37 @@ public final class JavaClass implements JavaNode {
return parent;
}
@Override
public JavaClass getTopParentClass() {
if (cls.contains(AType.ANONYMOUS_CLASS)) {
// moved to usage class
return getParentForAnonymousClass();
}
return parent == null ? this : parent.getTopParentClass();
public JavaClass getOriginalTopParentClass() {
return parent == null ? this : parent.getOriginalTopParentClass();
}
private JavaClass getParentForAnonymousClass() {
AnonymousClassAttr attr = cls.get(AType.ANONYMOUS_CLASS);
ClassNode topParentClass = attr.getOuterCls().getTopParentClass();
return getRootDecompiler().convertClassNode(topParentClass);
/**
* Return top parent class which contains code of this class.
* Code parent can be different from original parent after move or inline
*
* @return this if already a top class
*/
@Override
public JavaClass getTopParentClass() {
JavaClass codeParent = getCodeParent();
return codeParent == null ? this : codeParent.getTopParentClass();
}
/**
* Return parent class which contains code of this class.
* Code parent can be different for original parent after move or inline
*/
public @Nullable JavaClass getCodeParent() {
AnonymousClassAttr anonymousClsAttr = cls.get(AType.ANONYMOUS_CLASS);
if (anonymousClsAttr != null) {
// moved to usage class
return getRootDecompiler().convertClassNode(anonymousClsAttr.getOuterCls());
}
InlinedAttr inlinedAttr = cls.get(AType.INLINED);
if (inlinedAttr != null) {
return getRootDecompiler().convertClassNode(inlinedAttr.getInlineCls());
}
return parent;
}
public AccessInfo getAccessInfo() {
@@ -301,7 +334,7 @@ public final class JavaClass implements JavaNode {
@Override
public void removeAlias() {
this.cls.getClassInfo().removeAlias();
cls.removeAlias();
}
@Override
@@ -5,6 +5,7 @@ import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeNodeRef;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.FieldNode;
@@ -74,6 +75,11 @@ public final class JavaField implements JavaNode {
return false;
}
@Override
public ICodeNodeRef getCodeNodeRef() {
return field;
}
/**
* Internal API. Not Stable!
*/
@@ -10,6 +10,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeNodeRef;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.info.AccessInfo;
@@ -116,6 +117,11 @@ public final class JavaMethod implements JavaNode {
return false;
}
@Override
public ICodeNodeRef getCodeNodeRef() {
return mth;
}
/**
* Internal API. Not Stable!
*/
@@ -3,9 +3,12 @@ package jadx.api;
import java.util.List;
import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeNodeRef;
public interface JavaNode {
ICodeNodeRef getCodeNodeRef();
String getName();
String getFullName();
@@ -18,8 +21,7 @@ public interface JavaNode {
List<JavaNode> getUseIn();
default void removeAlias() {
}
void removeAlias();
boolean isOwnCodeAnnotation(ICodeAnnotation ann);
}
@@ -1,36 +1,91 @@
package jadx.api;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;
import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeNodeRef;
import jadx.core.dex.info.PackageInfo;
import jadx.core.dex.nodes.PackageNode;
public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
private final String name;
private final PackageNode pkgNode;
private final List<JavaClass> classes;
private final List<JavaPackage> subPkgs;
JavaPackage(String name, List<JavaClass> classes) {
this.name = name;
JavaPackage(PackageNode pkgNode, List<JavaClass> classes, List<JavaPackage> subPkgs) {
this.pkgNode = pkgNode;
this.classes = classes;
this.subPkgs = subPkgs;
}
@Override
public String getName() {
return name;
return pkgNode.getAliasPkgInfo().getName();
}
@Override
public String getFullName() {
// TODO: store full package name
return name;
return pkgNode.getAliasPkgInfo().getFullName();
}
public String getRawName() {
return pkgNode.getPkgInfo().getName();
}
public String getRawFullName() {
return pkgNode.getPkgInfo().getFullName();
}
public List<JavaPackage> getSubPackages() {
return subPkgs;
}
public List<JavaClass> getClasses() {
return classes;
}
public boolean isRoot() {
return pkgNode.isRoot();
}
public boolean isLeaf() {
return pkgNode.isLeaf();
}
public boolean isDefault() {
return getFullName().isEmpty();
}
public void rename(String alias) {
pkgNode.rename(alias);
}
@Override
public void removeAlias() {
pkgNode.removeAlias();
}
public boolean isParentRenamed() {
PackageInfo parent = pkgNode.getPkgInfo().getParentPkg();
PackageInfo aliasParent = pkgNode.getAliasPkgInfo().getParentPkg();
return !Objects.equals(parent, aliasParent);
}
@Override
public ICodeNodeRef getCodeNodeRef() {
return pkgNode;
}
@Internal
public PackageNode getPkgNode() {
return pkgNode;
}
@Override
public JavaClass getDeclaringClass() {
return null;
@@ -48,7 +103,16 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
@Override
public List<JavaNode> getUseIn() {
return Collections.emptyList();
List<JavaNode> list = new ArrayList<>();
addUseIn(list);
return list;
}
public void addUseIn(List<JavaNode> list) {
list.addAll(classes);
for (JavaPackage subPkg : subPkgs) {
subPkg.addUseIn(list);
}
}
@Override
@@ -58,7 +122,7 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
@Override
public int compareTo(@NotNull JavaPackage o) {
return name.compareTo(o.name);
return pkgNode.compareTo(o.pkgNode);
}
@Override
@@ -70,16 +134,16 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
return false;
}
JavaPackage that = (JavaPackage) o;
return name.equals(that.name);
return pkgNode.equals(that.pkgNode);
}
@Override
public int hashCode() {
return name.hashCode();
return pkgNode.hashCode();
}
@Override
public String toString() {
return name;
return pkgNode.toString();
}
}
@@ -4,8 +4,10 @@ import java.util.Collections;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeNodeRef;
import jadx.api.metadata.annotations.VarNode;
import jadx.api.metadata.annotations.VarRef;
import jadx.core.dex.instructions.args.ArgType;
@@ -32,10 +34,15 @@ public class JavaVariable implements JavaNode {
}
@Override
public String getName() {
public @Nullable String getName() {
return varNode.getName();
}
@Override
public ICodeNodeRef getCodeNodeRef() {
return varNode;
}
@ApiStatus.Internal
public VarNode getVarNode() {
return varNode;
@@ -70,6 +77,11 @@ public class JavaVariable implements JavaNode {
return Collections.singletonList(mth);
}
@Override
public void removeAlias() {
varNode.setName(null);
}
@Override
public boolean isOwnCodeAnnotation(ICodeAnnotation ann) {
if (ann.getAnnType() == ICodeAnnotation.AnnType.VAR_REF) {
@@ -62,6 +62,10 @@ public class ResourceFile {
return deobfName != null ? deobfName : name;
}
public void setDeobfName(String resFullName) {
this.deobfName = resFullName;
}
public ResourceType getType() {
return type;
}
@@ -74,13 +78,20 @@ public class ResourceFile {
this.zipRef = zipRef;
}
public void setAlias(ResourceEntry ri) {
int index = name.lastIndexOf('.');
deobfName = String.format("res/%s%s/%s%s",
ri.getTypeName(),
ri.getConfig(),
ri.getKeyName(),
index == -1 ? "" : name.substring(index));
public boolean setAlias(ResourceEntry ri) {
StringBuilder sb = new StringBuilder();
sb.append("res/").append(ri.getTypeName()).append(ri.getConfig());
sb.append("/").append(ri.getKeyName());
int lastDot = name.lastIndexOf('.');
if (lastDot != -1) {
sb.append(name.substring(lastDot));
}
String alias = sb.toString();
if (!alias.equals(name)) {
setDeobfName(alias);
return true;
}
return false;
}
public ZipRef getZipRef() {
@@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory;
import jadx.api.ResourceFile.ZipRef;
import jadx.api.impl.SimpleCodeInfo;
import jadx.api.plugins.CustomResourcesLoader;
import jadx.api.plugins.utils.ZipSecurity;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.Utils;
@@ -140,9 +141,23 @@ public final class ResourcesLoader {
if (file == null || file.isDirectory()) {
return;
}
// Try to load the resources with a custom loader first
for (CustomResourcesLoader loader : jadxRef.getCustomResourcesLoaders()) {
if (loader.load(this, list, file)) {
LOG.debug("Custom loader used for {}", file.getAbsolutePath());
return;
}
}
// If no custom decoder was able to decode the resources, use the default decoder
defaultLoadFile(list, file, "");
}
public void defaultLoadFile(List<ResourceFile> list, File file, String subDir) {
if (FileUtils.isZipFile(file)) {
ZipSecurity.visitZipEntries(file, (zipFile, entry) -> {
addEntry(list, file, entry);
addEntry(list, file, entry, subDir);
return null;
});
} else {
@@ -151,13 +166,13 @@ public final class ResourcesLoader {
}
}
private void addEntry(List<ResourceFile> list, File zipFile, ZipEntry entry) {
public void addEntry(List<ResourceFile> list, File zipFile, ZipEntry entry, String subDir) {
if (entry.isDirectory()) {
return;
}
String name = entry.getName();
ResourceType type = ResourceType.getFileType(name);
ResourceFile rf = ResourceFile.createResourceFile(jadxRef, name, type);
ResourceFile rf = ResourceFile.createResourceFile(jadxRef, subDir + name, type);
if (rf != null) {
rf.setZipRef(new ZipRef(zipFile, name));
list.add(rf);
@@ -1,6 +1,6 @@
package jadx.api.args;
public enum DeobfuscationMapFileMode {
public enum GeneratedRenamesMappingFileMode {
/**
* Load if found, don't save (default)
@@ -22,6 +22,10 @@ public enum DeobfuscationMapFileMode {
*/
IGNORE;
public static GeneratedRenamesMappingFileMode getDefault() {
return READ;
}
public boolean shouldRead() {
return this == READ || this == READ_OR_SAVE;
}
@@ -0,0 +1,11 @@
package jadx.api.args;
public enum IntegerFormat {
AUTO,
DECIMAL,
HEXADECIMAL;
public boolean isHexadecimal() {
return this == HEXADECIMAL;
}
}
@@ -0,0 +1,22 @@
package jadx.api.args;
/**
* Resources original name source (for deobfuscation)
*/
public enum ResourceNameSource {
/**
* Automatically select best name (default)
*/
AUTO,
/**
* Force use resources provided names
*/
RESOURCES,
/**
* Force use resources names from R class
*/
CODE,
}
@@ -0,0 +1,36 @@
package jadx.api.args;
public enum UserRenamesMappingsMode {
/**
* Just read, user can save manually (default)
*/
READ,
/**
* Read and autosave after every change
*/
READ_AND_AUTOSAVE_EVERY_CHANGE,
/**
* Read and autosave before exiting the app or closing the project
*/
READ_AND_AUTOSAVE_BEFORE_CLOSING,
/**
* Don't load and don't save
*/
IGNORE;
public static UserRenamesMappingsMode getDefault() {
return READ;
}
public boolean shouldRead() {
return this != IGNORE;
}
public boolean shouldWrite() {
return this == READ_AND_AUTOSAVE_EVERY_CHANGE || this == READ_AND_AUTOSAVE_BEFORE_CLOSING;
}
}
@@ -0,0 +1,69 @@
package jadx.api.data;
public enum CommentStyle {
/**
* <pre>
* // comment
* </pre>
*/
LINE("// ", "// ", ""),
// @formatter:off
/**
* <pre>
* /*
* * comment
* *&#47;
* </pre>
*/
// @formatter:on
BLOCK("/*\n * ", " * ", "\n */"),
/**
* <pre>
* /* comment *&#47;
* </pre>
*/
BLOCK_CONDENSED("/* ", " * ", " */"),
// @formatter:off
/**
* <pre>
* /**
* * comment
* *&#47;
* </pre>
*/
// @formatter:on
JAVADOC("/**\n * ", " * ", "\n */"),
/**
* <pre>
* /** comment *&#47;
* </pre>
*/
JAVADOC_CONDENSED("/** ", " * ", " */");
private final String start;
private final String onNewLine;
private final String end;
CommentStyle(String start, String onNewLine, String end) {
this.start = start;
this.onNewLine = onNewLine;
this.end = end;
}
public String getStart() {
return start;
}
public String getOnNewLine() {
return onNewLine;
}
public String getEnd() {
return end;
}
}
@@ -10,4 +10,6 @@ public interface ICodeComment extends Comparable<ICodeComment> {
IJavaCodeRef getCodeRef();
String getComment();
CommentStyle getStyle();
}
@@ -7,4 +7,6 @@ public interface ICodeData {
List<ICodeComment> getComments();
List<ICodeRename> getRenames();
boolean isEmpty();
}
@@ -0,0 +1,6 @@
package jadx.api.data;
public interface IRenameNode {
void rename(String newName);
}
@@ -3,6 +3,7 @@ package jadx.api.data.impl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import jadx.api.data.CommentStyle;
import jadx.api.data.ICodeComment;
import jadx.api.data.IJavaCodeRef;
import jadx.api.data.IJavaNodeRef;
@@ -13,15 +14,25 @@ public class JadxCodeComment implements ICodeComment {
@Nullable
private IJavaCodeRef codeRef;
private String comment;
private CommentStyle style = CommentStyle.LINE;
public JadxCodeComment(IJavaNodeRef nodeRef, String comment) {
this(nodeRef, null, comment);
}
public JadxCodeComment(IJavaNodeRef nodeRef, String comment, CommentStyle style) {
this(nodeRef, null, comment, style);
}
public JadxCodeComment(IJavaNodeRef nodeRef, @Nullable IJavaCodeRef codeRef, String comment) {
this(nodeRef, codeRef, comment, CommentStyle.LINE);
}
public JadxCodeComment(IJavaNodeRef nodeRef, @Nullable IJavaCodeRef codeRef, String comment, CommentStyle style) {
this.nodeRef = nodeRef;
this.codeRef = codeRef;
this.comment = comment;
this.style = style;
}
public JadxCodeComment() {
@@ -56,6 +67,15 @@ public class JadxCodeComment implements ICodeComment {
this.comment = comment;
}
@Override
public CommentStyle getStyle() {
return style;
}
public void setStyle(CommentStyle style) {
this.style = style;
}
@Override
public int compareTo(@NotNull ICodeComment other) {
int cmpNodeRef = this.getNodeRef().compareTo(other.getNodeRef());
@@ -73,6 +93,7 @@ public class JadxCodeComment implements ICodeComment {
return "JadxCodeComment{" + nodeRef
+ ", ref=" + codeRef
+ ", comment='" + comment + '\''
+ ", style=" + style
+ '}';
}
}
@@ -28,4 +28,9 @@ public class JadxCodeData implements ICodeData {
public void setRenames(List<ICodeRename> renames) {
this.renames = renames;
}
@Override
public boolean isEmpty() {
return comments.isEmpty() && renames.isEmpty();
}
}
@@ -85,4 +85,12 @@ public class JadxCodeRename implements ICodeRename {
public int hashCode() {
return 31 * getNodeRef().hashCode() + Objects.hashCode(getCodeRef());
}
@Override
public String toString() {
return "JadxCodeRename{" + nodeRef
+ ", codeRef=" + codeRef
+ ", newName='" + newName + '\''
+ '}';
}
}
@@ -0,0 +1,29 @@
package jadx.api.deobf;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.PackageNode;
import jadx.core.dex.nodes.RootNode;
public interface IAliasProvider {
default void init(RootNode root) {
// optional
}
String forPackage(PackageNode pkg);
String forClass(ClassNode cls);
String forField(FieldNode fld);
String forMethod(MethodNode mth);
/**
* Optional method to set initial max indexes loaded from mapping
*/
default void initIndexes(int pkg, int cls, int fld, int mth) {
// optional
}
}
@@ -0,0 +1,31 @@
package jadx.api.deobf;
import jadx.api.deobf.impl.CombineDeobfConditions;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.PackageNode;
import jadx.core.dex.nodes.RootNode;
/**
* Utility interface to simplify merging several rename conditions to build {@link IRenameCondition}
* instance with {@link CombineDeobfConditions#combine(IDeobfCondition...)}.
*/
public interface IDeobfCondition {
enum Action {
NO_ACTION,
FORCE_RENAME,
FORBID_RENAME,
}
void init(RootNode root);
Action check(PackageNode pkg);
Action check(ClassNode cls);
Action check(FieldNode fld);
Action check(MethodNode mth);
}
@@ -0,0 +1,20 @@
package jadx.api.deobf;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.PackageNode;
import jadx.core.dex.nodes.RootNode;
public interface IRenameCondition {
void init(RootNode root);
boolean shouldRename(PackageNode pkg);
boolean shouldRename(ClassNode cls);
boolean shouldRename(FieldNode fld);
boolean shouldRename(MethodNode mth);
}
@@ -0,0 +1,40 @@
package jadx.api.deobf.impl;
import jadx.api.deobf.IRenameCondition;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.PackageNode;
import jadx.core.dex.nodes.RootNode;
public class AlwaysRename implements IRenameCondition {
public static final IRenameCondition INSTANCE = new AlwaysRename();
private AlwaysRename() {
}
@Override
public void init(RootNode root) {
}
@Override
public boolean shouldRename(PackageNode pkg) {
return true;
}
@Override
public boolean shouldRename(ClassNode cls) {
return true;
}
@Override
public boolean shouldRename(FieldNode fld) {
return true;
}
@Override
public boolean shouldRename(MethodNode mth) {
return true;
}
}
@@ -0,0 +1,44 @@
package jadx.api.deobf.impl;
import java.util.function.BiPredicate;
import jadx.api.deobf.IRenameCondition;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.IDexNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.PackageNode;
import jadx.core.dex.nodes.RootNode;
public class AnyRenameCondition implements IRenameCondition {
private final BiPredicate<String, IDexNode> predicate;
public AnyRenameCondition(BiPredicate<String, IDexNode> predicate) {
this.predicate = predicate;
}
@Override
public void init(RootNode root) {
}
@Override
public boolean shouldRename(PackageNode pkg) {
return predicate.test(pkg.getAliasPkgInfo().getName(), pkg);
}
@Override
public boolean shouldRename(ClassNode cls) {
return predicate.test(cls.getAlias(), cls);
}
@Override
public boolean shouldRename(FieldNode fld) {
return predicate.test(fld.getAlias(), fld);
}
@Override
public boolean shouldRename(MethodNode mth) {
return predicate.test(mth.getAlias(), mth);
}
}
@@ -0,0 +1,73 @@
package jadx.api.deobf.impl;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import jadx.api.deobf.IDeobfCondition;
import jadx.api.deobf.IRenameCondition;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.PackageNode;
import jadx.core.dex.nodes.RootNode;
public class CombineDeobfConditions implements IRenameCondition {
public static IRenameCondition combine(List<IDeobfCondition> conditions) {
return new CombineDeobfConditions(conditions);
}
public static IRenameCondition combine(IDeobfCondition... conditions) {
return new CombineDeobfConditions(Arrays.asList(conditions));
}
private final List<IDeobfCondition> conditions;
private CombineDeobfConditions(List<IDeobfCondition> conditions) {
if (conditions == null || conditions.isEmpty()) {
throw new IllegalArgumentException("Conditions list can't be empty");
}
this.conditions = conditions;
}
private boolean combineFunc(Function<IDeobfCondition, IDeobfCondition.Action> check) {
for (IDeobfCondition c : conditions) {
switch (check.apply(c)) {
case NO_ACTION:
// ignore
break;
case FORCE_RENAME:
return true;
case FORBID_RENAME:
return false;
}
}
return false;
}
@Override
public void init(RootNode root) {
conditions.forEach(c -> c.init(root));
}
@Override
public boolean shouldRename(PackageNode pkg) {
return combineFunc(c -> c.check(pkg));
}
@Override
public boolean shouldRename(ClassNode cls) {
return combineFunc(c -> c.check(cls));
}
@Override
public boolean shouldRename(FieldNode fld) {
return combineFunc(c -> c.check(fld));
}
@Override
public boolean shouldRename(MethodNode mth) {
return combineFunc(c -> c.check(mth));
}
}
@@ -21,9 +21,6 @@ public class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter
private Map<Integer, ICodeAnnotation> annotations = Collections.emptyMap();
private Map<Integer, Integer> lineMap = Collections.emptyMap();
public AnnotatedCodeWriter() {
}
public AnnotatedCodeWriter(JadxArgs args) {
super(args);
}
@@ -35,9 +32,9 @@ public class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter
@Override
public AnnotatedCodeWriter addMultiLine(String str) {
if (str.contains(NL)) {
buf.append(str.replace(NL, NL + indentStr));
line += StringUtils.countMatches(str, NL);
if (str.contains(newLineStr)) {
buf.append(str.replace(newLineStr, newLineStr + indentStr));
line += StringUtils.countMatches(str, newLineStr);
offset = 0;
} else {
buf.append(str);
@@ -84,7 +81,7 @@ public class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter
@Override
protected void addLine() {
buf.append(NL);
buf.append(newLineStr);
line++;
offset = 0;
}
@@ -154,8 +151,6 @@ public class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter
@Override
public ICodeInfo finish() {
removeFirstEmptyLine();
processDefinitionAnnotations();
validateAnnotations();
String code = buf.toString();
buf = null;
@@ -167,18 +162,6 @@ public class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter
return annotations;
}
private void processDefinitionAnnotations() {
if (!annotations.isEmpty()) {
annotations.forEach((k, v) -> {
if (v instanceof NodeDeclareRef) {
NodeDeclareRef declareRef = (NodeDeclareRef) v;
declareRef.setDefPos(k);
declareRef.getNode().setDefPosition(k);
}
});
}
}
private void validateAnnotations() {
if (annotations.isEmpty()) {
return;
@@ -14,38 +14,39 @@ import jadx.api.metadata.ICodeNodeRef;
import jadx.core.utils.Utils;
/**
* CodeWriter implementation without meta information support (only strings builder)
* CodeWriter implementation without meta information support
*/
public class SimpleCodeWriter implements ICodeWriter {
private static final Logger LOG = LoggerFactory.getLogger(SimpleCodeWriter.class);
private static final String[] INDENT_CACHE = {
"",
INDENT_STR,
INDENT_STR + INDENT_STR,
INDENT_STR + INDENT_STR + INDENT_STR,
INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR,
INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR + INDENT_STR,
};
protected StringBuilder buf = new StringBuilder();
protected String indentStr = "";
protected int indent = 0;
private final boolean insertLineNumbers;
public SimpleCodeWriter() {
this.insertLineNumbers = false;
}
protected final boolean insertLineNumbers;
protected final String singleIndentStr;
protected final String newLineStr;
public SimpleCodeWriter(JadxArgs args) {
this.insertLineNumbers = args.isInsertDebugLines();
this.singleIndentStr = args.getCodeIndentStr();
this.newLineStr = args.getCodeNewLineStr();
if (insertLineNumbers) {
incIndent(3);
add(indentStr);
}
}
/**
* Constructor with JadxArgs should be used.
*/
@Deprecated
public SimpleCodeWriter() {
this.insertLineNumbers = false;
this.singleIndentStr = JadxArgs.DEFAULT_INDENT_STR;
this.newLineStr = JadxArgs.DEFAULT_NEW_LINE_STR;
}
@Override
public boolean isMetadataSupported() {
return false;
@@ -96,8 +97,8 @@ public class SimpleCodeWriter implements ICodeWriter {
@Override
public SimpleCodeWriter addMultiLine(String str) {
if (str.contains(NL)) {
buf.append(str.replace(NL, NL + indentStr));
if (str.contains(newLineStr)) {
buf.append(str.replace(newLineStr, newLineStr + indentStr));
} else {
buf.append(str);
}
@@ -130,12 +131,12 @@ public class SimpleCodeWriter implements ICodeWriter {
@Override
public SimpleCodeWriter addIndent() {
add(INDENT_STR);
add(singleIndentStr);
return this;
}
protected void addLine() {
buf.append(NL);
buf.append(newLineStr);
}
protected SimpleCodeWriter addLineIndent() {
@@ -144,12 +145,7 @@ public class SimpleCodeWriter implements ICodeWriter {
}
private void updateIndent() {
int curIndent = indent;
if (curIndent < INDENT_CACHE.length) {
this.indentStr = INDENT_CACHE[curIndent];
} else {
this.indentStr = Utils.strRepeat(INDENT_STR, curIndent);
}
this.indentStr = Utils.strRepeat(singleIndentStr, indent);
}
@Override
@@ -219,17 +215,17 @@ public class SimpleCodeWriter implements ICodeWriter {
@Override
public ICodeInfo finish() {
removeFirstEmptyLine();
String code = buf.toString();
String code = getStringWithoutFirstEmptyLine();
buf = null;
return new SimpleCodeInfo(code);
}
protected void removeFirstEmptyLine() {
int len = NL.length();
if (buf.length() > len && buf.substring(0, len).equals(NL)) {
buf.delete(0, len);
private String getStringWithoutFirstEmptyLine() {
int len = newLineStr.length();
if (buf.length() > len && buf.substring(0, len).equals(newLineStr)) {
return buf.substring(len);
}
return buf.toString();
}
@Override
@@ -249,7 +245,6 @@ public class SimpleCodeWriter implements ICodeWriter {
@Override
public String getCodeStr() {
removeFirstEmptyLine();
return buf.toString();
}
@@ -0,0 +1,60 @@
package jadx.api.impl.passes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.plugins.pass.JadxPass;
import jadx.api.plugins.pass.types.JadxDecompilePass;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.exceptions.JadxException;
public class DecompilePassWrapper extends AbstractVisitor implements IPassWrapperVisitor {
private static final Logger LOG = LoggerFactory.getLogger(DecompilePassWrapper.class);
private final JadxDecompilePass decompilePass;
public DecompilePassWrapper(JadxDecompilePass decompilePass) {
this.decompilePass = decompilePass;
}
@Override
public JadxPass getPass() {
return decompilePass;
}
@Override
public void init(RootNode root) throws JadxException {
try {
decompilePass.init(root);
} catch (Throwable e) {
LOG.error("Error in decompile pass init: {}", this, e);
}
}
@Override
public boolean visit(ClassNode cls) throws JadxException {
try {
return decompilePass.visit(cls);
} catch (Throwable e) {
LOG.error("Error in decompile pass: {}, class: {}", this, cls, e);
return false;
}
}
@Override
public void visit(MethodNode mth) throws JadxException {
try {
decompilePass.visit(mth);
} catch (Throwable e) {
LOG.error("Error in decompile pass: {}, method: {}", this, mth, e);
}
}
@Override
public String getName() {
return decompilePass.getInfo().getName();
}
}
@@ -0,0 +1,9 @@
package jadx.api.impl.passes;
import jadx.api.plugins.pass.JadxPass;
import jadx.core.dex.visitors.IDexTreeVisitor;
public interface IPassWrapperVisitor extends IDexTreeVisitor {
JadxPass getPass();
}
@@ -0,0 +1,39 @@
package jadx.api.impl.passes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.plugins.pass.JadxPass;
import jadx.api.plugins.pass.types.JadxPreparePass;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.exceptions.JadxException;
public class PreparePassWrapper extends AbstractVisitor implements IPassWrapperVisitor {
private static final Logger LOG = LoggerFactory.getLogger(PreparePassWrapper.class);
private final JadxPreparePass preparePass;
public PreparePassWrapper(JadxPreparePass preparePass) {
this.preparePass = preparePass;
}
@Override
public JadxPass getPass() {
return preparePass;
}
@Override
public void init(RootNode root) throws JadxException {
try {
preparePass.init(root);
} catch (Exception e) {
LOG.error("Error in prepare pass init: {}", this, e);
}
}
@Override
public String getName() {
return preparePass.getInfo().getName();
}
}
@@ -6,10 +6,12 @@ public interface ICodeAnnotation {
CLASS,
FIELD,
METHOD,
PKG,
VAR,
VAR_REF,
DECLARATION,
OFFSET
OFFSET,
END // class or method body end
}
AnnType getAnnType();
@@ -32,6 +32,22 @@ public class NodeDeclareRef implements ICodeAnnotation {
return AnnType.DECLARATION;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof NodeDeclareRef)) {
return false;
}
return node.equals(((NodeDeclareRef) o).node);
}
@Override
public int hashCode() {
return node.hashCode();
}
@Override
public String toString() {
return "NodeDeclareRef{" + node + '}';
@@ -0,0 +1,21 @@
package jadx.api.metadata.annotations;
import jadx.api.metadata.ICodeAnnotation;
public class NodeEnd implements ICodeAnnotation {
public static final NodeEnd VALUE = new NodeEnd();
private NodeEnd() {
}
@Override
public AnnType getAnnType() {
return AnnType.END;
}
@Override
public String toString() {
return "END";
}
}
@@ -6,16 +6,14 @@ import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.function.BiFunction;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeAnnotation.AnnType;
import jadx.api.metadata.ICodeMetadata;
import jadx.api.metadata.ICodeNodeRef;
import jadx.api.metadata.annotations.NodeDeclareRef;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.Utils;
public class CodeMetadataStorage implements ICodeMetadata {
@@ -55,7 +53,7 @@ public class CodeMetadataStorage implements ICodeMetadata {
}
@Override
public @Nullable ICodeAnnotation searchUp(int position, ICodeAnnotation.AnnType annType) {
public @Nullable ICodeAnnotation searchUp(int position, AnnType annType) {
for (ICodeAnnotation v : navMap.tailMap(position, true).values()) {
if (v.getAnnType() == annType) {
return v;
@@ -65,7 +63,7 @@ public class CodeMetadataStorage implements ICodeMetadata {
}
@Override
public @Nullable ICodeAnnotation searchUp(int position, int limitPos, ICodeAnnotation.AnnType annType) {
public @Nullable ICodeAnnotation searchUp(int position, int limitPos, AnnType annType) {
for (ICodeAnnotation v : navMap.subMap(position, true, limitPos, true).values()) {
if (v.getAnnType() == annType) {
return v;
@@ -99,28 +97,40 @@ public class CodeMetadataStorage implements ICodeMetadata {
@Override
public ICodeNodeRef getNodeAt(int position) {
return navMap.tailMap(position, true)
.values().stream()
.flatMap(CodeMetadataStorage::mapEnclosingNode)
.findFirst().orElse(null);
int nesting = 0;
for (ICodeAnnotation ann : navMap.tailMap(position, true).values()) {
switch (ann.getAnnType()) {
case END:
nesting++;
break;
case DECLARATION:
ICodeNodeRef node = ((NodeDeclareRef) ann).getNode();
AnnType nodeType = node.getAnnType();
if (nodeType == AnnType.CLASS || nodeType == AnnType.METHOD) {
if (nesting == 0) {
return node;
}
nesting--;
}
break;
}
}
return null;
}
@Override
public ICodeNodeRef getNodeBelow(int position) {
return navMap.headMap(position, true).descendingMap()
.values().stream()
.flatMap(CodeMetadataStorage::mapEnclosingNode)
.findFirst().orElse(null);
}
private static Stream<ICodeNodeRef> mapEnclosingNode(ICodeAnnotation ann) {
if (ann instanceof NodeDeclareRef) {
ICodeNodeRef node = ((NodeDeclareRef) ann).getNode();
if (node instanceof ClassNode || node instanceof MethodNode) {
return Stream.of(node);
for (ICodeAnnotation ann : navMap.headMap(position, true).descendingMap().values()) {
if (ann.getAnnType() == AnnType.DECLARATION) {
ICodeNodeRef node = ((NodeDeclareRef) ann).getNode();
AnnType nodeType = node.getAnnType();
if (nodeType == AnnType.CLASS || nodeType == AnnType.METHOD) {
return node;
}
}
}
return Stream.empty();
return null;
}
@Override
@@ -135,7 +145,7 @@ public class CodeMetadataStorage implements ICodeMetadata {
@Override
public String toString() {
return "CodeMetadata{lines=" + lines
+ ", annotations=\n" + Utils.listToString(navMap.entrySet(), "\n") + "\n}";
return "CodeMetadata{\nlines=" + lines
+ "\nannotations=\n " + Utils.listToString(navMap.descendingMap().entrySet(), "\n ") + "\n}";
}
}
@@ -0,0 +1,19 @@
package jadx.api.plugins;
import java.io.Closeable;
import java.io.File;
import java.util.List;
import jadx.api.ResourceFile;
import jadx.api.ResourcesLoader;
public interface CustomResourcesLoader extends Closeable {
/**
* Load resources from file to list of ResourceFile
*
* @param list list to add loaded resources
* @param file file to load
* @return true if file was loaded
*/
boolean load(ResourcesLoader loader, List<ResourceFile> list, File file);
}
@@ -0,0 +1,26 @@
package jadx.api.plugins;
import jadx.api.plugins.pass.types.JadxAfterLoadPass;
import jadx.api.plugins.pass.types.JadxPreparePass;
/**
* Base interface for all jadx plugins
* <br>
* To create new plugin implement this interface and add to resources
* a {@code META-INF/services/jadx.api.plugins.JadxPlugin} file with a full name of your class.
*/
public interface JadxPlugin {
/**
* Method for provide plugin information, like name and description.
* Can be invoked several times.
*/
JadxPluginInfo getPluginInfo();
/**
* Init plugin.
* Use {@link JadxPluginContext} to register passes, code inputs and options.
* For long operation, prefer {@link JadxPreparePass} or {@link JadxAfterLoadPass} instead.
*/
void init(JadxPluginContext context);
}
@@ -0,0 +1,50 @@
package jadx.api.plugins;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.plugins.data.IJadxPlugins;
import jadx.api.plugins.events.IJadxEvents;
import jadx.api.plugins.gui.JadxGuiContext;
import jadx.api.plugins.input.JadxCodeInput;
import jadx.api.plugins.options.JadxPluginOptions;
import jadx.api.plugins.pass.JadxPass;
public interface JadxPluginContext {
JadxArgs getArgs();
JadxDecompiler getDecompiler();
void addPass(JadxPass pass);
void addCodeInput(JadxCodeInput codeInput);
void registerOptions(JadxPluginOptions options);
/**
* Function to calculate hash of all options which can change output code.
* Hash for input files ({@link JadxArgs#getInputFiles()}) and registered options
* calculated by default implementations.
*/
void registerInputsHashSupplier(Supplier<String> supplier);
/**
* Access to jadx-gui specific methods
*/
@Nullable
JadxGuiContext getGuiContext();
/**
* Subscribe and send events
*/
IJadxEvents events();
/**
* Access to registered plugins and runtime data
*/
IJadxPlugins plugins();
}
@@ -4,20 +4,26 @@ public class JadxPluginInfo {
private final String pluginId;
private final String name;
private final String description;
private final String homepage;
/**
* Conflicting plugins should have same 'provides' property, only one will be loaded
* Conflicting plugins should have the same 'provides' property; only one will be loaded
*/
private final String provides;
public JadxPluginInfo(String id, String name, String description) {
this(id, name, description, id);
this(id, name, description, "", id);
}
public JadxPluginInfo(String pluginId, String name, String description, String provides) {
this(pluginId, name, description, "", provides);
}
public JadxPluginInfo(String pluginId, String name, String description, String homepage, String provides) {
this.pluginId = pluginId;
this.name = name;
this.description = description;
this.homepage = homepage;
this.provides = provides;
}
@@ -33,6 +39,10 @@ public class JadxPluginInfo {
return description;
}
public String getHomepage() {
return homepage;
}
public String getProvides() {
return provides;
}
@@ -0,0 +1,55 @@
package jadx.api.plugins;
import java.util.Objects;
import org.jetbrains.annotations.Nullable;
public class JadxPluginInfoBuilder {
private String pluginId;
private String name;
private String description;
private String homepage = "";
private @Nullable String provides;
/**
* Start building method
*/
public static JadxPluginInfoBuilder pluginId(String pluginId) {
JadxPluginInfoBuilder builder = new JadxPluginInfoBuilder();
builder.pluginId = Objects.requireNonNull(pluginId);
return builder;
}
private JadxPluginInfoBuilder() {
}
public JadxPluginInfoBuilder name(String name) {
this.name = Objects.requireNonNull(name);
return this;
}
public JadxPluginInfoBuilder description(String description) {
this.description = Objects.requireNonNull(description);
return this;
}
public JadxPluginInfoBuilder homepage(String homepage) {
this.homepage = homepage;
return this;
}
public JadxPluginInfoBuilder provides(String provides) {
this.provides = provides;
return this;
}
public JadxPluginInfo build() {
Objects.requireNonNull(pluginId, "PluginId is required");
Objects.requireNonNull(name, "Name is required");
Objects.requireNonNull(description, "Description is required");
if (provides == null) {
provides = pluginId;
}
return new JadxPluginInfo(pluginId, name, description, homepage, provides);
}
}
@@ -0,0 +1,12 @@
package jadx.api.plugins.data;
import jadx.api.plugins.JadxPlugin;
public interface IJadxPlugins {
JadxPluginRuntimeData getById(String pluginId);
JadxPluginRuntimeData getProviding(String provideId);
<P extends JadxPlugin> P getInstance(Class<P> pluginCls);
}
@@ -0,0 +1,38 @@
package jadx.api.plugins.data;
import java.io.Closeable;
import java.nio.file.Path;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.JadxPluginInfo;
import jadx.api.plugins.input.ICodeLoader;
import jadx.api.plugins.input.JadxCodeInput;
import jadx.api.plugins.options.JadxPluginOptions;
/**
* Runtime plugin data.
*/
public interface JadxPluginRuntimeData {
boolean isInitialized();
String getPluginId();
JadxPlugin getPluginInstance();
JadxPluginInfo getPluginInfo();
List<JadxCodeInput> getCodeInputs();
@Nullable
JadxPluginOptions getOptions();
String getInputsHash();
/**
* Convenient method to simplify code loading from custom files.
*/
ICodeLoader loadCodeFiles(List<Path> files, @Nullable Closeable closeable);
}
@@ -0,0 +1,6 @@
package jadx.api.plugins.events;
public interface IJadxEvent {
JadxEventType<? extends IJadxEvent> getType();
}
@@ -0,0 +1,18 @@
package jadx.api.plugins.events;
import java.util.function.Consumer;
public interface IJadxEvents {
/**
* Send an event object.
* For public event types check {@link JadxEvents} class.
*/
void send(IJadxEvent event);
/**
* Register listener for specific event.
* For public event types check {@link JadxEvents} class.
*/
<E extends IJadxEvent> void addListener(JadxEventType<E> eventType, Consumer<E> listener);
}
@@ -0,0 +1,9 @@
package jadx.api.plugins.events;
public abstract class JadxEventType<T extends IJadxEvent> {
public static <E extends IJadxEvent> JadxEventType<E> create() {
return new JadxEventType<>() {
};
}
}
@@ -0,0 +1,32 @@
package jadx.api.plugins.events;
import jadx.api.plugins.events.types.NodeRenamedByUser;
import jadx.api.plugins.events.types.ReloadProject;
import jadx.api.plugins.events.types.ReloadSettingsWindow;
import jadx.api.plugins.gui.ISettingsGroup;
import jadx.api.plugins.gui.JadxGuiSettings;
import static jadx.api.plugins.events.JadxEventType.create;
/**
* Typed and extendable enumeration of event types
*/
public class JadxEvents {
/**
* Notify about renaming done by user (GUI only).
*/
public static final JadxEventType<NodeRenamedByUser> NODE_RENAMED_BY_USER = create();
/**
* Request reload of a current project (GUI only).
*/
public static final JadxEventType<ReloadProject> RELOAD_PROJECT = create();
/**
* Request reload of a settings window (GUI only).
* Useful for a reload custom settings group which was set with
* {@link JadxGuiSettings#setCustomSettingsGroup(ISettingsGroup)}.
*/
public static final JadxEventType<ReloadSettingsWindow> RELOAD_SETTINGS_WINDOW = create();
}
@@ -0,0 +1,72 @@
package jadx.api.plugins.events.types;
import org.jetbrains.annotations.Nullable;
import jadx.api.metadata.ICodeNodeRef;
import jadx.api.plugins.events.IJadxEvent;
import jadx.api.plugins.events.JadxEventType;
import jadx.api.plugins.events.JadxEvents;
public class NodeRenamedByUser implements IJadxEvent {
private final ICodeNodeRef node;
private final String oldName;
private final String newName;
/**
* Optional JRenameNode instance
*/
private @Nullable Object renameNode;
/**
* Request reset name to original
*/
private boolean resetName = false;
public NodeRenamedByUser(ICodeNodeRef node, String oldName, String newName) {
this.node = node;
this.oldName = oldName;
this.newName = newName;
}
public ICodeNodeRef getNode() {
return node;
}
public String getOldName() {
return oldName;
}
public String getNewName() {
return newName;
}
public Object getRenameNode() {
return renameNode;
}
public void setRenameNode(Object renameNode) {
this.renameNode = renameNode;
}
public boolean isResetName() {
return resetName;
}
public void setResetName(boolean resetName) {
this.resetName = resetName;
}
@Override
public JadxEventType<NodeRenamedByUser> getType() {
return JadxEvents.NODE_RENAMED_BY_USER;
}
@Override
public String toString() {
return "NodeRenamedByUser{" + node
+ ", '" + oldName + "' -> '" + newName + '\''
+ (resetName ? ", reset name" : "")
+ '}';
}
}
@@ -0,0 +1,24 @@
package jadx.api.plugins.events.types;
import jadx.api.plugins.events.IJadxEvent;
import jadx.api.plugins.events.JadxEventType;
import jadx.api.plugins.events.JadxEvents;
public class ReloadProject implements IJadxEvent {
public static final ReloadProject EVENT = new ReloadProject();
private ReloadProject() {
// singleton
}
@Override
public JadxEventType<ReloadProject> getType() {
return JadxEvents.RELOAD_PROJECT;
}
@Override
public String toString() {
return "RELOAD_PROJECT";
}
}

Some files were not shown because too many files have changed in this diff Show More