Compare commits

...

533 Commits

Author SHA1 Message Date
Skylot c4c3d42d16 build: fix release workflow 2024-11-11 22:46:31 +00:00
Skylot 58c36de8c2 build: add release github workflow 2024-11-10 20:15:34 +00:00
Skylot b4ca566a19 build: exclude shadow jar from publishing to maven 2024-11-10 20:15:34 +00:00
Skylot e644bad758 chore: update dependencies 2024-11-10 20:15:34 +00:00
Skylot dd86abcc5e chore: remove LGTM config 2024-11-10 17:26:24 +00:00
Skylot f0513a1a55 fix: insert new filled array insn before first usage (#2340) 2024-11-07 20:02:41 +00:00
Skylot be6cb573b1 feat(plugins): allow to set minimum required jadx version in plugin info (#2314) 2024-11-06 16:30:22 +00:00
pubiqq 5d064d3e50 feat(res): improve resource names (PR #2316) 2024-11-06 18:08:35 +03:00
pubiqq 15b6309e2c fix: support "fall-through to default" case in switch-over-string (PR #2338) 2024-11-05 21:41:03 +00:00
Skylot 417bb7a7e9 feat: add method to update all blocks related info in method (#2335) 2024-11-02 22:08:26 +00:00
Ahmet Bilal Can 4c74e8e300 feat: give ability to plugins to edit blocks before locking (PR #2336)
Plugins can use `.before('BlockFinisher')` to edit blocks before they are locked.
2024-11-02 21:38:57 +00:00
Skylot 57238de6ff feat(cli): add option to disable plugins (#2277) 2024-11-01 20:13:34 +00:00
Skylot 313c4a121a fix: improve negation condition checks for switch over string (#2333) 2024-11-01 16:33:28 +00:00
pubiqq 39912398fc fix: unwrap consts in switch-over-string (PR #2332) 2024-10-31 21:24:19 +00:00
Skylot 7544d1a113 fix: prevent endless loop in pre header insertion mod (#2300) 2024-10-31 20:00:06 +00:00
pubiqq cfbe5ab672 fix: fix default branch position in switch-over-string (PR #2331) 2024-10-31 19:58:35 +00:00
Ruffalo Lavoisier 2661b91a6f feat(gui): create Frida hooking snippet for all methods in the class (PR #2328) 2024-10-30 17:59:29 +00:00
Skylot 61578e8793 fix(gui): correct tabs filter in "Close others" tab action (#2330) 2024-10-30 17:20:16 +00:00
Skylot cc6a893402 feat: allow to disable installed plugins (#2277) 2024-10-28 23:35:28 +03:00
pubiqq 4d8a5d6671 fix(core): fix primitive-to-primitive conversions (PR #2326) 2024-10-28 20:33:10 +00:00
Skylot 982307b1ac fix(gui): use correct section filter in plugins list 2024-10-25 19:01:43 +01:00
Skylot 37054dc84e fix(gui): load plugins settings in temp context without UI (#2206) 2024-10-25 18:51:38 +01:00
Skylot 343e2c531a chore: expand input dirs in ConvertArscFile 2024-10-25 18:12:48 +01:00
pubiqq 6fa5d247f0 fix(res): update Android attrs to API 35 (PR #2318) 2024-10-25 18:08:36 +01:00
pubiqq 688dea0c50 fix(deobf): update TLDs (PR#2320) 2024-10-25 16:44:07 +01:00
Skylot c0815b12bc chore(gui): add missing ref text in untranslated messages (#2319)
fix
2024-10-24 20:33:52 +03:00
JustFor 233f8692b2 fix(gui): update Messages_zh_CN.properties (PR #2319)
1. New text is translated simultaneously, two untranslated texts remain (no English text)
2. Adjusted and unified part of the symbol, into the full Angle symbol.
2024-10-24 18:31:14 +01:00
Skylot e5be41b9cc fix(gui): use another implementation for font dialog (#2310) 2024-10-22 21:32:14 +01:00
Skylot 3788e4ef3a fix(gui): in settings row reduce space between description and value 2024-10-22 18:52:42 +01:00
Skylot 32855f4511 fix(gui): improve plugins preferences group 2024-10-22 18:51:19 +01:00
Skylot 3d5e225274 fix: clear temp root dir instead delete (#2312) 2024-10-22 18:01:52 +01:00
nitram84 8a34d973ff build: use jadx-gui as a library in plugins (PR #2310)
* fix(gui): fix javadoc issues

* feat(gui): export jadx-gui as a library

* fix(gui): fix javadoc issues

* fix(gui): remove invalid characters for javadoc

* add jadx-library also for jadx-cli and jadx-script-ide modules

---------

Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
2024-10-22 17:24:57 +01:00
Skylot a43b3282ef fix(gui): update check fixed to match current artifact naming 2024-10-17 21:57:20 +01:00
Skylot 249801880c feat(api): allow to get method code (#2305) 2024-10-17 19:20:07 +01:00
Skylot 742d30d770 chore: resolve warnings in SmaliArea class 2024-10-16 17:29:36 +01:00
Skylot 267413332a chore: resolve KtLint deprecation warning 2024-10-16 17:23:09 +01:00
Skylot f9c94f27f1 chore: update dependencies 2024-10-16 17:01:03 +01:00
Skylot d2131d9640 build: gradle wrapper validation not needed, check done in setup-java action 2024-10-16 17:00:43 +01:00
Skylot 548821c4df build: allow to change java toolchain for build and tests 2024-10-16 16:54:28 +01:00
pubiqq 958ab245ae fix(res): don't rename resource entries when useRawResName = true (PR #2306) 2024-10-14 20:32:12 +01:00
Skylot 8f3cc3e8c1 fix: add missing null check in codegen for classes generated by jadx 2024-10-14 19:19:31 +01:00
Skylot b872ffd1b9 fix: replace patched zip early in input files (#2302) 2024-10-12 20:53:09 +01:00
Skylot c5263f92fe fix(gui): send select tab event before code jump (#2292) 2024-10-12 21:23:53 +03:00
pubiqq 1475e887c8 fix: update reserved keywords (PR #2301) 2024-10-11 20:47:34 +01:00
Skylot c21cabcba7 fix: use temp dir env var only in apps 2024-10-10 22:57:40 +03:00
Skylot 063af8cd62 feat(api): add JadxArgs property to adjust xml security checks (#2291) 2024-10-10 22:57:40 +03:00
Skylot 2d10537050 chore: update dependencies 2024-10-10 22:57:40 +03:00
pubiqq 3814951408 feat: improve better name algorithm (PR #2299) 2024-10-10 20:01:57 +01:00
qfalconer 964bd62d35 feat: adding automatic patching for semi-corrupted APKs (PR #2298)
* feat: patching semi-corrupted APKs

* fix: using secure temp file creation

* fix: using TWR when handling the files
2024-10-10 17:53:26 +01:00
xiaojye 3ed2df828f feat(gui): add button to go to Android Manifest (PR #2296)
* feat: Add button to go to Android Manifest

* fix: Change the text from 'Go to Manifest' to 'Go to AndroidManifest.xml' and replace icon source
2024-10-10 17:11:08 +01:00
Skylot b26abdc851 feat(plugins): get config and cache dirs for plugins 2024-09-29 21:55:05 +01:00
Skylot 90185fd947 feat(plugins): get a main window reference as JFrame 2024-09-29 21:54:24 +01:00
Skylot 681f8a98b5 fix: improve checks for restore new filled array (#2289) 2024-09-28 16:52:08 +01:00
Skylot 0b225238fb feat: support restore of switch over string (basic case)(#2288) 2024-09-27 21:11:38 +01:00
Skylot a7649dda7a chore: update gradle and dependencies 2024-09-27 21:08:38 +01:00
pubiqq b5e3dcf70f feat: add the option to always use source file name as class name alias (PR #2287) 2024-09-23 22:47:08 +01:00
Skylot 7abbc81886 fix: improve switch out block search if all method exits are inside (#2264) 2024-09-22 21:35:40 +01:00
Skylot 9c30aeacdb refactor: split region maker 2024-09-22 20:23:03 +01:00
Skylot 8f27de4d0e chore: update dependencies 2024-09-21 22:00:03 +01:00
Skylot 02b69d2d29 fix(gui): prevent old refs leak in shortcuts controller 2024-09-21 21:53:31 +01:00
Skylot e6fde48b69 fix: don't add same 'loaded from:' comment for inner classes 2024-09-21 20:28:18 +01:00
Skylot 109dea0857 feat: support inner class contruction with outer instance (#2253) 2024-09-21 20:27:16 +01:00
Skylot 1d34328dd3 fix(build): disable cache for core tests to allow reruns (#2283) 2024-09-20 21:44:55 +01:00
Skylot ef4f1d3ed4 fix: ignore debug lines hints if numbers was adjusted for method 2024-09-20 21:36:13 +01:00
Skylot 23696d3971 fix: use type from new-instance if differ from constructor call (#2285) 2024-09-20 21:34:21 +01:00
Andy Smith efa2f5d172 feat(gui): limit search to a package (PR #2284)
* Add isDescendantOf and getJavaPackage helper functions

* Add i18n strings for search package

* Added search package to options in SearchSettings

* Add package limiting to each search provider

* Add package search to dialog and logic to get package by string.

* Added search option to package context menu

* Fix spotlessJavaCheck complaints

* Revert changes to individual search providers and add filter to base provider
2024-09-20 12:31:20 +03:00
Skylot 699ceb197e fix: improve condition branch checks in loops (#2274) 2024-09-16 20:37:34 +01:00
Skylot 5c83c22501 feat(java-input): support StackMapTable to get stack info for unvisited jumps (#2271) 2024-09-14 22:45:31 +01:00
Skylot 7bb5c0a859 fix: protect class deps from loading in different decompilation mode 2024-09-13 21:25:11 +01:00
Skylot 603863403f fix: do not add custom passes for fallback and simple modes (#2276) 2024-09-13 21:01:19 +01:00
Skylot 889a945cf1 fix: additional checks for class signature (#2272) 2024-09-12 20:29:05 +01:00
Skylot fd80e03809 fix: check if debug info offset is invalid (#1653) 2024-09-11 19:54:49 +01:00
Skylot b5807082d9 chore: update gradle and dependencies 2024-09-11 19:54:49 +01:00
Skylot 5d1f0b8cae feat(res): support grammar inflection flag in res config (#2270) 2024-09-08 21:49:13 +01:00
Skylot 3f9aa34057 fix(gui): resolve old objects reference leak in TabsController 2024-09-07 01:21:46 +03:00
Skylot e2c860f260 refactor(res): use list instead map to store entries offsets 2024-09-07 01:21:46 +03:00
Skylot 0938351d97 feat(res): support compact resource entries (#2268) 2024-09-07 01:21:42 +03:00
pubiqq 937dd20794 feat(res): support 16-bit entry offsets (PR #2269) 2024-09-06 23:21:21 +01:00
pubiqq ea5e87560a feat(res): improve error message for unsupported ResTable flags (PR #2266) 2024-09-05 19:13:26 +01:00
Skylot 5fbbf2150e fix(res): prevent duplication of ARSC entries (#2263) 2024-09-04 21:31:37 +01:00
Skylot 0e11bffe82 chore: update dependencies 2024-09-04 20:02:56 +01:00
Skylot ba9af5c288 fix(gui): minor fixes for code jumps 2024-09-04 19:59:57 +01:00
Skylot cca706c94f fix: improve 'continue' insertion for switch in loop (#2249) 2024-09-01 23:02:22 +01:00
Skylot 2df69bbfb4 fix(gui): prevent UI stuck on class load (#2259) 2024-08-31 22:30:18 +01:00
Skylot f5307636ef fix(gui): merge full class name tokens for constructors (#2261) 2024-08-30 22:39:43 +03:00
Mino 9a39b70a46 fix(gui): Quick Tabs Optimization (PR #2242)
* optimize tabs reorder

* restructure based on quick tabs architecture

* code formatting

* log all exceptions from background executor

* various improvements

---------

Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
2024-08-30 20:33:05 +01:00
pubiqq e63808bc4b fix: improve checking of access modifiers (PR #2255) 2024-08-20 16:45:28 +03:00
Skylot 847225a6a9 fix: improve try/catch temp edges injection (#2247) 2024-08-17 21:09:31 +01:00
Skylot eee354a3ab chore: update gradle and dependencies 2024-08-17 21:01:27 +01:00
Skylot 1cc00a54f3 fix(gui): use correct translation in rename dialog (#2254) 2024-08-17 21:58:39 +03:00
pubiqq ffdad1b652 fix: improve checking of access modifiers for methods (PR #2252) 2024-08-15 23:20:07 +01:00
pubiqq 9a8ec76989 fix: improve checking of access modifiers for classes (PR #2251) 2024-08-15 19:59:44 +01:00
Skylot 0be5b2cea9 refactor(tests): add debug checks switch to jadx args 2024-08-13 22:39:48 +01:00
Skylot c94201be4a fix: improve switch out search in loop (#2246) 2024-08-12 22:06:03 +01:00
Skylot 1051dacb1e refactor(tests): migrate from Hamcrest to AssertJ 2024-08-11 21:55:56 +01:00
Skylot a2bfe9bbe8 chore: add openrewrite gradle plugin to improve code quality 2024-08-11 21:10:01 +01:00
Skylot 8c6ec3bccc fix(gui): trim also leading spaces in paths from file dialog (#2244) 2024-08-10 19:24:05 +01:00
Skylot 015876b790 fix(gui): trim trailing spaces in input files (#2244) 2024-08-10 00:31:25 +01:00
Skylot 280f3870a9 fix: handle quick return on branched constructor (#2240) 2024-08-09 22:54:13 +01:00
Skylot e9d770ae9e fix: use correct approach to get prev block on path (#2239) 2024-08-09 22:51:37 +01:00
Skylot 5f1bd1d9ba chore: migrate gradle shadow plugin 2024-08-08 20:44:34 +01:00
pubiqq 60fb458024 fix: improve inlining synthetic accessors (PR #2243)
* fix: fix inlining synthetic accessors

* add test, undo changes in InsnNode

---------

Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
2024-08-08 20:27:08 +01:00
Skylot 1b08779536 chore: update dependencies 2024-08-07 19:06:58 +01:00
Skylot c37e39a819 chore: code improvements by cleanthat 2024-08-07 00:34:22 +01:00
Skylot 2d58fbd4b1 chore: forbid use ArrayList as a variable type 2024-08-07 00:28:39 +01:00
Mino ffbf800404 feat(gui): Quick Tabs Overhaul (PR #2241)
* restructure quick tabs code

* code formatting

* display open tabs

* added bookmark tabs feature

* fix tabs pin and bookmark not saved

* fix NPE treeModel not initialized

* Fix hardcoded strings

* remove unused statement

* fix NPE again

* added bookmark overlay

* preserve tabs order

* fix context menu actions

* remove unnecessary public modifier

* save tabs in tabbedpane order

* remove unreferenced tabs

* Update jadx-gui/src/main/java/jadx/gui/ui/tab/TabComponent.java

---------

Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2024-08-06 23:47:25 +01:00
dependabot[bot] 500aa8a68d build(deps): bump gradle/actions from 3 to 4 (PR #2238)
Bumps [gradle/actions](https://github.com/gradle/actions) from 3 to 4.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/v3...v4)

---
updated-dependencies:
- dependency-name: gradle/actions
  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-08-05 18:35:39 +01:00
Skylot 58e8268126 fix: workaround to make method inline deterministic (#1089) 2024-08-04 22:46:46 +01:00
Skylot f9da6e00ed fix(gui): add VM flags to fix UI ghosting (#2225) 2024-08-04 19:40:14 +01:00
Skylot 821cc668c7 fix: don't rerun SSA transform in ConstructorVisitor (#2236) 2024-08-02 21:26:25 +01:00
Skylot 287ba49008 fix(gui): show folding actions in code popup menu (#2234) 2024-08-02 17:13:16 +01:00
pubiqq 115e563a2b fix: improve checking if methods are inline for FixAccessModifiers (PR #2235) 2024-08-01 21:45:52 +01:00
Mino 1669200e62 feat(gui): smali code folding (PR #2233)
* added smali code folding

* remove unnecessary public modifiers

* improve code

* undo method extract

---------

Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
2024-08-01 19:33:37 +01:00
Mino 6ab224ea0d feat(gui): pin tabs (PR #2230)
* add ability to pin tabs

* save pinned tabs with Save Project action

* further prevent closing pinned tabs

* add translation entries

* prevent pinning start page

* add pinned tabs tree view

* properly dispose of quickTabsTree

* restructure code

* more unpin context menu items
2024-08-01 18:06:45 +01:00
pubiqq 61855a7ea1 fix: make detailed var info deterministic (PR #2231) 2024-07-31 22:16:07 +01:00
Mino bda3119e86 fix(gui): horizontal scrolling in Linux (PR #2229)
* fix horizontal scrolling in linux

* improve code

---------

Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
2024-07-31 17:02:00 +01:00
Mino b26abed686 feat(gui): export resource/class/package (PR #2228)
* feat: export resource

* feat: export class

* restructure code: introduce enum for exporting classes

* feat: export package

* feat: export resource folder

* check directory exists before creation

* apply code formatting

* fix code formatting

* Apply suggestions from code review

---------

Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
2024-07-31 17:00:54 +01:00
Skylot c179beee95 chore: update dependencies 2024-07-28 20:57:37 +01:00
Skylot 04a454094b fix: improve exception handlers checks (#2086) 2024-07-27 22:09:14 +01:00
Skylot 33bcbfec41 chore(docs): minor fixes 2024-07-27 22:09:13 +01:00
Skylot ec645d80b1 fix(mappings): try to prevent mapping file reset on export exception, refactor and fix code to avoid NPE (#2220)(#2226) 2024-07-25 17:44:23 +01:00
Jan S. a8d889de3d fix(launch4j): do not overwrite Java heap configuration from applicationDefaultJvmArgs (PR #2218) 2024-07-20 20:46:45 +01:00
Skylot ec0bf701c8 fix(build): fix hiding console in Windows (#2196)
Regression after migration to shadow jar in #1868,
instead CreateStartScripts task from 'application' plugin
'startShadowScripts' task should be modified.
2024-07-19 00:19:47 +03:00
Skylot ad4dd116be chore: update gradle and dependencies 2024-07-19 00:19:47 +03:00
Skylot ef79f266c8 fix(build): fix gitlab build 2024-07-19 00:19:43 +03:00
Jan S 366225f9be fix(quark): fix automated installation and check exit code of executed external commands (#2119)(PR #2216) 2024-07-12 18:39:17 +01:00
Skylot 730db0d24f fix(build): do not wrap jar in launch4j (#2186) 2024-07-08 22:17:06 +01:00
Iscle 05fb77e9bd feat(gui): add button to go to Application class (#2208)(PR #2213)
* feat: Add button to go to Application class

Icons from: https://intellij-icons.jetbrains.design/

* fix: Rename "goto" to "go_to" to keep things consistent
2024-07-08 18:36:45 +01:00
Iscle bbabfa0354 fix(gui): fix Xposed args code generation (PR #2212)
* Rename .java to .kt

* fix: Fix wrong function arguments when generating kotlin xposed code
2024-07-08 18:34:28 +01:00
Jan S 96bd9f0f17 fix(xml): AXML/Manifest parsing improvements (PR #2211)
* log and ignore decodeValue errors

* skip extra data in package header

* ResourceTypes.h
2024-07-06 19:27:47 +03:00
qfalconer fd5b397b40 fix(xml): allow for non-standard attributes sizes and avoid index exceptions when decoding some strings (PR #2210)
More lenient AXML parsing: allow for non-standard attributes sizes and avoid index exceptions when decoding some strings

* The attributes size of an XML element is now accounted for. This size must be at least 20 (0x14) bytes but can be greater. Extra bytes are just skipped. When decoding a string, if such decoding is impossible a placeholder string is returned instead of throwing an exception. This is necessary because some malware purposely add android:tag attributes with invalid string index to throw parsers off. They also employ non-standard attribute sizes.

* Minor code restyling

---------

Co-authored-by: qfalconer <knm241@gmail.com>
2024-07-03 18:50:15 +01:00
Artem Zhiganov f5e3a261b4 fix(gui): update russian translation (PR #2209)
Co-authored-by: SVolf <dev@thunderdog.ru>
2024-06-28 18:39:23 +01:00
JustFor 52a884608a fix(gui): update Messages_zh_CN.properties (PR #2203)
sync new texts.
2024-06-14 18:52:31 +01:00
Iscle 74ddfde950 feat(gui): allow to check for unstable releases (PR #2200)
* feat: gui: convert JadxUpdate to Kotlin

* feat: gui: allow updater to check for latest unstable artifacts

* fix: remove nullable operator from onUpdate() interface
2024-06-11 22:49:44 +01:00
Skylot 9aacb4f312 fix: config dir was used instead cache dir 2024-06-11 20:55:49 +01:00
Jiaxin Peng 82e2104f3c fix(gui): support filtering files with multiple extensions in file dialog (PR #2185)
* fix(gui): support filtering files with multiple extensions in file dialog

* lint
2024-05-19 17:47:35 +01:00
Skylot 09fa35f144 feat: allow to change config and cache dirs with env vars (#2159) 2024-04-27 21:48:42 +01:00
Skylot f2a6a1e942 build: update JDK to 21 for windows artifacts 2024-04-27 17:40:06 +01:00
Andrei Kudryavtsev b85900aa3d feat: move AAB support to separate plugin (PR #2165)
* wip: finished with factories

* wip: bundleconfig.pb

* wip: jadx-aab-input, separate BundleConfig parser

* wip: removed test apks

* wip: proto xml pretty print

* wip: fixed getNamedValues NPE

* minor fixes

* spotless

* enabled zip64 for gui shadow jar

* spotless

* spotless

* reverted manifest identification since signature parsing not working at the moment

* replace static methods with new API methods

---------

Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
2024-04-26 21:54:54 +01:00
Jan S 37a42d1418 fix(gui): show correct content of binary resources in hex view (#2160) (PR #2166) 2024-04-24 19:29:52 +01:00
Jan S 07dde05337 fix(build): configure launch4j to not change current directory (#2162) (PR #2163) 2024-04-24 18:52:08 +01:00
Nick 8618214c7f docs: improve installation section (PR #2161)
* Update README.md

* Update README.md
2024-04-24 18:43:05 +01:00
Skylot b80f32a36f fix(smali-input): compile one smali file at a time to avoid 64k limit (#2158) 2024-04-23 22:14:59 +01:00
Skylot ce527ed753 fix(build): add missing files in bundle 2024-04-20 21:04:17 +01:00
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
1486 changed files with 73944 additions and 27915 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
+36 -32
View File
@@ -8,56 +8,60 @@ 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: 21
- name: Set jadx version
run: |
JADX_LAST_TAG=$(git describe --abbrev=0 --tags)
JADX_VERSION="${JADX_LAST_TAG:1}.$GITHUB_RUN_NUMBER-${GITHUB_SHA:0:8}"
JADX_REV=$(git rev-list --count HEAD)
JADX_VERSION="r${JADX_REV}.${GITHUB_SHA:0:7}"
echo "JADX_VERSION=$JADX_VERSION" >> $GITHUB_ENV
- uses: burrunan/gradle-cache-action@v1
name: Build with Gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build
run: ./gradlew dist distWin
env:
TERM: dumb
with:
arguments: clean dist copyExe
JADX_BUILD_JAVA_VERSION: 11
- 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
# Upload unpacked files for now
path: build/jadx/**/*
if-no-files-found: error
retention-days: 30
retention-days: 14
- name: Save exe artifact
uses: actions/upload-artifact@v3
- name: Save Windows bundle artifact
uses: actions/upload-artifact@v4
with:
name: ${{ format('jadx-gui-{0}-no-jre-win.exe', env.JADX_VERSION) }}
path: build/*.exe
name: ${{ format('jadx-gui-{0}-no-jre-win', env.JADX_VERSION) }}
# Upload unpacked files for now
path: jadx-gui/build/jadx-gui-win/*
if-no-files-found: error
retention-days: 30
retention-days: 14
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: 21
- name: Print Java version
shell: bash
@@ -66,21 +70,21 @@ jobs:
- name: Set jadx version
shell: bash
run: |
JADX_LAST_TAG=$(git describe --abbrev=0 --tags)
JADX_VERSION="${JADX_LAST_TAG:1}.$GITHUB_RUN_NUMBER-${GITHUB_SHA:0:8}"
JADX_REV=$(git rev-list --count HEAD)
JADX_VERSION="r${JADX_REV}.${GITHUB_SHA:0:7}"
echo "JADX_VERSION=$JADX_VERSION" >> $GITHUB_ENV
- uses: gradle/gradle-build-action@v2
name: Build with Gradle
env:
TERM: dumb
with:
arguments: clean dist -PbundleJRE=true
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Save exe bundle artifact
uses: actions/upload-artifact@v3
- name: Build
run: ./gradlew dist -PbundleJRE=true
- name: Save Windows with JRE bundle artifact
uses: actions/upload-artifact@v4
with:
name: ${{ format('jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}
path: jadx-gui/build/*-with-jre-win/*
# Upload unpacked files for now
path: jadx-gui/build/jadx-gui-with-jre-win/*
if-no-files-found: error
retention-days: 30
retention-days: 14
+16 -12
View File
@@ -8,21 +8,25 @@ 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: 21
- uses: burrunan/gradle-cache-action@v1
name: Build with Gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build
run: ./gradlew build dist distWin
env:
TERM: dumb
with:
arguments: clean build dist copyExe --warning-mode=all
JADX_BUILD_JAVA_VERSION: 11
+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 +0,0 @@
name: "Validate Gradle Wrapper"
on: [push]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
+92
View File
@@ -0,0 +1,92 @@
name: Release
on:
push:
tags:
- "v*.*.*"
# additional permissions for provided GitHub token to create new release
permissions:
contents: write
jobs:
build-release-win-bundle:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: oracle-actions/setup-java@v1
with:
release: 23
- name: Set jadx version
uses: actions/github-script@v7
with:
script: |
const jadxVersion = context.ref.split('/').pop().substring(1)
core.exportVariable('JADX_VERSION', jadxVersion);
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build
run: ./gradlew dist -PbundleJRE=true
- name: Save JRE bundle artifact
uses: actions/upload-artifact@v4
with:
name: ${{ format('jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}
path: ${{ format('build/distWinWithJre/jadx-gui-{0}-with-jre-win.zip', env.JADX_VERSION) }}
if-no-files-found: error
retention-days: 1
release:
needs: build-release-win-bundle
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Set jadx version and release name
uses: actions/github-script@v7
with:
script: |
const jadxVersion = context.ref.split('/').pop().substring(1)
core.exportVariable('JADX_VERSION', jadxVersion);
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build
run: ./gradlew dist distWin
env:
JADX_BUILD_JAVA_VERSION: 11
- name: Download Windows JRE bundle
uses: actions/download-artifact@v4
with:
name: ${{ format('jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}
path: ${{ format('build/jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}
- run: |
cd build
pwd
ls -l
ls jadx-gui-*-with-jre-win
mv jadx-gui-*-with-jre-win/jadx-gui-*-with-jre-win.zip .
mv distWin/jadx-gui-*-win.zip .
ls -l *.zip
- name: Release
uses: softprops/action-gh-release@v2
with:
name: ${{ env.JADX_VERSION }}
draft: true
fail_on_unmatched_files: true
files: build/jadx-*.zip
+2
View File
@@ -22,11 +22,13 @@ classes/
idea/
.gradle/
node_modules/
.vscode/
jadx-output/
*-tmp/
**/tmp/
*.jobf
*.jadx
*.class
*.dump
+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 distWin
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 distWin
java-21:
stage: test
image: eclipse-temurin:21
script: ./gradlew clean build dist distWin
+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 -Dsun.java2d.noddraw=true -Dsun.java2d.d3d=false -Dsun.java2d.ddforcevram=true -Dsun.java2d.ddblit=false -Dswing.useflipBufferStrategy=True"/>
<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",
]
+10 -23
View File
@@ -3,7 +3,7 @@
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
@@ -23,13 +23,13 @@ include:
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
professional setting
## Our Responsibilities
@@ -45,25 +45,12 @@ threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at skylot@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Attribution
+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`
+129 -76
View File
@@ -2,22 +2,25 @@
## JADX
[![Build status](https://github.com/skylot/jadx/workflows/Build/badge.svg)](https://github.com/skylot/jadx/actions?query=workflow%3ABuild)
![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
@@ -48,24 +51,28 @@ 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 ![Arch Linux package](https://img.shields.io/archlinux/v/community/any/jadx?label=)
```bash
sudo pacman -S jadx
```
2. macOS ![homebrew version](https://img.shields.io/homebrew/v/jadx?label=)
```bash
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
```
- Arch Linux
[![Arch Linux package](https://img.shields.io/archlinux/v/extra/any/jadx)](https://archlinux.org/packages/extra/any/jadx/)
[![AUR Version](https://img.shields.io/aur/version/jadx-git)](https://aur.archlinux.org/packages/jadx-git)
```bash
sudo pacman -S jadx
```
- macOS
[![homebrew version](https://img.shields.io/homebrew/v/jadx)](https://formulae.brew.sh/formula/jadx)
```bash
brew install jadx
```
- Flathub
[![Flathub Version](https://img.shields.io/flathub/v/com.github.skylot.jadx)](https://flathub.org/apps/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
@@ -79,68 +86,114 @@ 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
-dr, --output-dir-res - output directory for resources
-r, --no-res - do not decode resources
-s, --no-src - do not decompile source code
--single-class - decompile a single class, full name, raw or alias
--single-class-output - file or dir for write if decompile a single class
--output-format - can be 'java' or 'json', default: java
-e, --export-gradle - save as android gradle project
-j, --threads-count - processing threads count, default: 4
-m, --decompilation-mode - code output mode:
'auto' - trying best options (default)
'restructure' - restore code structure (normal java code)
'simple' - simplified instructions (linear, with goto's)
'fallback' - raw instructions without modifications
--show-bad-code - show inconsistent code (incorrectly decompiled)
--no-imports - disable use of imports, always write entire package name
--no-debug-info - disable debug info
--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-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
--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:
'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-use-sourcename - use source file name as class name alias
--deobf-parse-kotlin-metadata - parse kotlin metadata to class and package 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),
'valid' - rename java identifiers to make them valid,
'printable' - remove non-printable chars from identifiers,
or single 'none' - to disable all renames
or single 'all' - to enable all (default)
--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)
-f, --fallback - set '--decompilation-mode' to 'fallback' (deprecated)
--use-dx - use dx/d8 to convert java bytecode
--comments-level - set code comments level, values: error, warn, info, debug, user-only, none, default: info
--log-level - set log level, values: quiet, progress, error, warn, info, debug, default: progress
-v, --verbose - verbose output (set --log-level to DEBUG)
-q, --quiet - turn off output (set --log-level to QUIET)
--version - print jadx version
-h, --help - print this help
-d, --output-dir - output directory
-ds, --output-dir-src - output directory for sources
-dr, --output-dir-res - output directory for resources
-r, --no-res - do not decode resources
-s, --no-src - do not decompile source code
--single-class - decompile a single class, full name, raw or alias
--single-class-output - file or dir for write if decompile a single class
--output-format - can be 'java' or 'json', default: java
-e, --export-gradle - save as android gradle project
-j, --threads-count - processing threads count, default: 4
-m, --decompilation-mode - code output mode:
'auto' - trying best options (default)
'restructure' - restore code structure (normal java code)
'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 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-restore-switch-over-string - don't restore switch over string
--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-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-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-source-name-as-class-name-alias - use source name as class name alias:
'always' - always use source name if it's available
'if-better' - use source name if it seems better than the current one
'never' - never use source name, even if it's available
--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),
'valid' - rename java identifiers to make them valid,
'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)
-f, --fallback - set '--decompilation-mode' to 'fallback' (deprecated)
--use-dx - use dx/d8 to convert java bytecode
--comments-level - set code comments level, values: error, warn, info, debug, user-only, none, default: info
--log-level - set log level, values: quiet, progress, error, warn, info, debug, default: progress
-v, --verbose - verbose output (set --log-level to DEBUG)
-q, --quiet - turn off output (set --log-level to QUIET)
--version - print jadx version
-h, --help - print this help
Plugin options (-P<name>=<value>):
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
dex-input: Load .dex and .apk files
- dex-input.verify-checksum - verify dex file checksum before load, values: [yes, no], default: yes
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
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
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, JAM_FILE, CSRG_FILE, TSRG_FILE, TSRG_2_FILE, PROGUARD_FILE, RECAF_SIMPLE_FILE, JOBF_FILE], default: AUTO
- rename-mappings.invert - invert mapping on load, values: [yes, no], default: no
smali-input: Load .smali files
- smali-input.api-level - Android API level, default: 27
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_CONFIG_DIR - custom config directory, using system by default
JADX_CACHE_DIR - custom cache directory, using system by default
JADX_TMP_DIR - custom temp directory, using system by default
Examples:
jadx -d out classes.dex
@@ -149,7 +202,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)
+4 -3
View File
@@ -2,6 +2,7 @@
## Reporting a Vulnerability
To report a security issue, please email `skylot@gmail.com` with a description of the issue, the steps you took to create the issue, affected versions, and, if known, mitigations for the issue.
We will check and respond within 3 working days. If the issue is confirmed as a vulnerability, we will apply required mitigations at the next release.
This project follows a 90 day disclosure timeline.
To report a security issue, please open a [new security advisory](https://github.com/skylot/jadx/security/advisories/new).
Please fill the steps you took to create the issue, affected versions, and, if known, mitigations for the issue.
We will check and respond within 3 working days.
If the issue is confirmed as a vulnerability, we will apply required mitigations at the next release.
-160
View File
@@ -1,160 +0,0 @@
plugins {
id 'com.github.ben-manes.versions' version '0.42.0'
id 'com.diffplug.spotless' version '6.8.0'
}
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.1'
testImplementation 'org.assertj:assertj-core:3.23.1'
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()
// Commented out for now since we're using a local mapping-io fork atm.
// maven {
// name 'FabricMC'
// url 'https://maven.fabricmc.net/'
// }
}
}
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)
+163
View File
@@ -0,0 +1,163 @@
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
val jadxBuildJavaVersion by extra { getBuildJavaVersion() }
fun getBuildJavaVersion(): Int? {
val envVarName = "JADX_BUILD_JAVA_VERSION"
val buildJavaVer = System.getenv(envVarName)?.toInt() ?: return null
if (buildJavaVer < 11) {
throw GradleException("'$envVarName' can't be set to lower than 11")
}
println("Set Java toolchain for jadx build to version '$buildJavaVer'")
return buildJavaVer
}
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")
}
from(layout.projectDirectory) {
include("README.md")
include("LICENSE")
}
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 distWin by tasks.registering(Zip::class) {
group = "jadx"
description = "Build Windows bundle"
val guiTask = tasks.getByPath("jadx-gui:copyDistWin")
dependsOn(guiTask)
from(guiTask.outputs)
destinationDirectory.set(layout.buildDirectory.dir("distWin"))
archiveFileName.set("jadx-gui-$jadxVersion-win.zip")
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
val distWinWithJre by tasks.registering(Zip::class) {
description = "Build Windows with JRE bundle"
val guiTask = tasks.getByPath(":jadx-gui:copyDistWinWithJre")
dependsOn(guiTask)
from(guiTask.outputs)
destinationDirectory.set(layout.buildDirectory.dir("distWinWithJre"))
archiveFileName.set("jadx-gui-$jadxVersion-with-jre-win.zip")
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
val dist by tasks.registering {
group = "jadx"
description = "Build jadx distribution zip bundles"
dependsOn(pack)
val os = DefaultNativePlatform.getCurrentOperatingSystem()
if (os.isWindows) {
if (project.hasProperty("bundleJRE")) {
println("Build win bundle with JRE")
dependsOn(distWinWithJre)
} else {
dependsOn(distWin)
}
}
}
val cleanBuildDir by tasks.registering(Delete::class) {
delete(layout.buildDirectory)
}
tasks.getByName("clean").dependsOn(cleanBuildDir)
-3
View File
@@ -1,3 +0,0 @@
plugins {
id 'groovy-gradle-plugin'
}
+13
View File
@@ -0,0 +1,13 @@
plugins {
`kotlin-dsl`
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21")
implementation("org.openrewrite:plugin:6.19.1")
}
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,63 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
plugins {
java
checkstyle
id("jadx-rewrite")
}
val jadxVersion: String by rootProject.extra
val jadxBuildJavaVersion: Int? by rootProject.extra
group = "io.github.skylot"
version = jadxVersion
dependencies {
implementation("org.slf4j:slf4j-api:2.0.16")
compileOnly("org.jetbrains:annotations:26.0.1")
testImplementation("ch.qos.logback:logback-classic:1.5.12")
testImplementation("org.assertj:assertj-core:3.26.3")
testImplementation("org.junit.jupiter:junit-jupiter:5.11.3")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testCompileOnly("org.jetbrains:annotations:26.0.1")
}
repositories {
mavenCentral()
// required for: aapt-proto, r8, smali
google()
}
java {
jadxBuildJavaVersion?.let { buildJavaVer ->
toolchain {
languageVersion = JavaLanguageVersion.of(buildJavaVer)
}
}
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(project.description ?: "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(project.properties["libEmail"].toString())
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")
}
@@ -0,0 +1,37 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
plugins {
id("org.openrewrite.rewrite")
}
repositories {
mavenCentral()
}
dependencies {
rewrite("org.openrewrite.recipe:rewrite-testing-frameworks:2.21.0")
rewrite("org.openrewrite.recipe:rewrite-logging-frameworks:2.15.1")
rewrite("org.openrewrite.recipe:rewrite-migrate-java:2.28.0")
rewrite("org.openrewrite.recipe:rewrite-static-analysis:1.19.0")
}
tasks {
rewrite {
// exclusion("src/test/java/jadx/tests/integration")
// activeRecipe("org.openrewrite.java.migrate.Java8toJava11")
// checkstyle auto fix
// activeRecipe("org.openrewrite.staticanalysis.CodeCleanup")
// setCheckstyleConfigFile(file("$rootDir/config/checkstyle/checkstyle.xml"))
// logging
// activeRecipe("org.openrewrite.java.logging.slf4j.Slf4jBestPractices")
// activeRecipe("org.openrewrite.java.logging.slf4j.LoggersNamedForEnclosingClass")
// activeRecipe("org.openrewrite.java.logging.slf4j.ParameterizedLogging")
// activeRecipe("org.openrewrite.java.logging.PrintStackTraceToLogError")
// testing
activeRecipe("org.openrewrite.java.testing.assertj.Assertj")
}
}
+6 -2
View File
@@ -120,7 +120,10 @@
<module name="SuppressWarningsHolder"/>
<module name="IllegalType"/>
<module name="IllegalType">
<property name="illegalClassNames" value="java.util.ArrayList, java.util.HashMap, java.util.HashSet,
java.util.LinkedHashMap, java.util.LinkedHashSet, java.util.TreeMap, java.util.TreeSet"/>
</module>
<module name="IllegalImport">
<property name="illegalClasses" value="jadx.core.utils.DebugUtils"/>
</module>
@@ -128,7 +131,8 @@
<property name="id" value="printstacktrace"/>
<property name="format" value="\.printStackTrace\(\)"/>
<property name="ignoreComments" value="true"/>
<property name="message" value="Using Throwable.printStackTrace() is forbidden. Use logger to print exception"/>
<property name="message"
value="Using Throwable.printStackTrace() is forbidden. Use logger to print exception"/>
</module>
</module>
+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"/>
+2 -4
View File
@@ -1,7 +1,7 @@
/*
* Generated on 11/22/21, 8:58 PM
*/
package jadx.gui.ui.codeearea;
package jadx.gui.ui.codearea;
import java.io.*;
import javax.swing.text.Segment;
@@ -9,7 +9,7 @@ import javax.swing.text.Segment;
import org.fife.ui.rsyntaxtextarea.*;
/**
/*
* 用于Smali代码高亮
* MartinKay@qq.com
*/
@@ -173,7 +173,6 @@ import org.fife.ui.rsyntaxtextarea.*;
zzAtEOF = false;
}
%}
Letter = [A-Za-z]
@@ -678,4 +677,3 @@ FLAG_ARRAY = (":array_"{SimpleName})
\n { addToken(start,zzStartRead-1, Token.COMMENT_EOL); addNullToken(); return firstToken; }
<<EOF>> { addToken(start,zzStartRead-1, Token.COMMENT_EOL); addNullToken(); return firstToken; }
}
+16 -13
View File
@@ -57,12 +57,12 @@
private int yychar;
/**
* the number of characters from the last newline up to the start of the
* the number of characters from the last newline up to the start of the
* matched text
*/
private int yycolumn;
/**
/**
* zzAtBOL == true <=> the scanner is currently at the beginning of a line
*/
private boolean zzAtBOL = true;
@@ -102,6 +102,9 @@
zzLexicalState = newState;
}
public final int yystate() {
return zzLexicalState;
}
/**
* Returns the text matched by the current regular expression.
@@ -112,12 +115,12 @@
/**
* Returns the character at position <tt>pos</tt> from the
* matched text.
*
* Returns the character at position <tt>pos</tt> from the
* matched text.
*
* It is equivalent to yytext().charAt(pos), but faster
*
* @param pos the position of the character to fetch.
* @param pos the position of the character to fetch.
* A value from 0 to yylength()-1.
*
* @return the character at position pos
@@ -138,8 +141,8 @@
/**
* Reports an error that occured while scanning.
*
* In a wellformed scanner (no or only correct usage of
* yypushback(int) and a match-all fallback rule) this method
* In a wellformed scanner (no or only correct usage of
* yypushback(int) and a match-all fallback rule) this method
* will only be called with things that "Can't Possibly Happen".
* If this method is called, something is seriously wrong
* (e.g. a JFlex bug producing a faulty scanner etc.).
@@ -159,7 +162,7 @@
}
--- throws clause
}
}
/**
@@ -206,12 +209,12 @@
zzAction = -1;
zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
--- start admin (lexstate etc)
zzForAction: {
while (true) {
--- next input, line, col, char count, next transition, isFinal action
zzAction = zzState;
zzMarkedPosL = zzCurrentPosL;
@@ -226,11 +229,11 @@
--- char count update
--- actions
default:
default:
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
zzAtEOF = true;
--- eofvalue
}
}
else {
--- no match
}
+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=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+31 -13
View File
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,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/platforms/jvm/plugins-application/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 +82,12 @@ 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -133,22 +134,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 +201,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 +217,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
+94 -89
View File
@@ -1,89 +1,94 @@
@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
@rem SPDX-License-Identifier: Apache-2.0
@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'
}
}
+49
View File
@@ -0,0 +1,49 @@
plugins {
id("jadx-java")
id("jadx-library")
id("application")
// use shadow only for application scripts, jar will be copied from jadx-gui
id("com.gradleup.shadow") version "8.3.5"
}
dependencies {
implementation(project(":jadx-core"))
implementation(project(":jadx-plugins-tools"))
implementation(project(":jadx-commons:jadx-app-commons"))
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"))
runtimeOnly(project(":jadx-plugins:jadx-aab-input"))
implementation("org.jcommander:jcommander:2.0")
implementation("ch.qos.logback:logback-classic:1.5.12")
}
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()
}
@@ -4,7 +4,7 @@ import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -13,23 +13,30 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterDescription;
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 +50,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,28 +85,69 @@ 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_CONFIG_DIR - custom config directory, using system by default");
out.println(" JADX_CACHE_DIR - custom cache directory, using system by default");
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();
Map<String, ParameterDescription> paramsMap = new LinkedHashMap<>(params.size());
Map<String, ParameterDescription> paramsMap = new HashMap<>(params.size());
int maxNamesLen = 0;
for (ParameterDescription p : params) {
paramsMap.put(p.getParameterized().getName(), p);
int len = p.getNames().length();
if (len > maxNamesLen) {
maxNamesLen = len;
String valueDesc = getValueDesc(p);
if (valueDesc != null) {
len += 1 + valueDesc.length();
}
maxNamesLen = Math.max(maxNamesLen, len);
}
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);
@@ -100,8 +156,12 @@ public class JCommanderWrapper<T> {
}
StringBuilder opt = new StringBuilder();
opt.append(" ").append(p.getNames());
String description = p.getDescription();
String valueDesc = getValueDesc(p);
if (valueDesc != null) {
opt.append(' ').append(valueDesc);
}
addSpaces(opt, maxNamesLen - opt.length());
String description = p.getDescription();
if (description.contains("\n")) {
String[] lines = description.split("\n");
opt.append("- ").append(lines[0]);
@@ -113,26 +173,26 @@ 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;
}
private static @Nullable String getValueDesc(ParameterDescription p) {
Parameter parameterAnnotation = p.getParameterAnnotation();
return parameterAnnotation == null ? null : parameterAnnotation.defaultValueDescription();
}
/**
* 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 +202,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 +231,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++;
}
}
}
}
@@ -187,13 +252,12 @@ 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) {
List<OptionDescription> descs = options.getOptionsDescriptions();
if (descs.isEmpty()) {
return false;
}
JadxPluginInfo pluginInfo = plugin.getPluginInfo();
out.append("\n ").append(k).append(") ");
out.append("\n ");
out.append(pluginInfo.getPluginId()).append(": ").append(pluginInfo.getDescription());
for (OptionDescription desc : descs) {
StringBuilder opt = new StringBuilder();
+57 -17
View File
@@ -1,41 +1,49 @@
package jadx.cli;
import java.util.Set;
import org.slf4j.Logger;
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.api.security.JadxSecurityFlag;
import jadx.api.security.impl.JadxSecurity;
import jadx.cli.LogHelper.LogLevelEnum;
import jadx.cli.plugins.JadxFilesGetter;
import jadx.commons.app.JadxCommonEnv;
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);
public static void main(String[] args) {
int result = 0;
int result = 1;
try {
result = execute(args);
} catch (JadxArgsValidateException e) {
LOG.error("Incorrect arguments: {}", e.getMessage());
result = 1;
} catch (Throwable e) {
LOG.error("Process error:", e);
result = 1;
} finally {
FileUtils.deleteTempRootDir();
System.exit(result);
}
}
public static int execute(String[] args) {
JadxCLIArgs jadxArgs = new JadxCLIArgs();
if (jadxArgs.processArgs(args)) {
return processAndSave(jadxArgs);
try {
JadxCLIArgs jadxArgs = new JadxCLIArgs();
if (jadxArgs.processArgs(args)) {
return processAndSave(jadxArgs);
}
return 0;
} catch (JadxArgsValidateException e) {
LOG.error("Incorrect arguments: {}", e.getMessage());
return 1;
} catch (Throwable e) {
LOG.error("Process error:", e);
return 1;
}
return 0;
}
private static int processAndSave(JadxCLIArgs cliArgs) {
@@ -43,7 +51,10 @@ public class JadxCLI {
LogHelper.setLogLevelsForLoadingStage();
JadxArgs jadxArgs = cliArgs.toJadxArgs();
jadxArgs.setCodeCache(new NoOpCodeCache());
jadxArgs.setCodeWriterProvider(SimpleCodeWriter::new);
jadxArgs.setPluginLoader(new JadxExternalPluginsLoader());
jadxArgs.setFilesGetter(JadxFilesGetter.INSTANCE);
initCodeWriterProvider(jadxArgs);
applyEnvVars(jadxArgs);
try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
jadx.load();
if (checkForErrors(jadx)) {
@@ -57,11 +68,39 @@ public class JadxCLI {
if (errorsCount != 0) {
jadx.printErrorsReport();
LOG.error("finished with errors, count: {}", errorsCount);
} else {
LOG.info("done");
return 1;
}
LOG.info("done");
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 void applyEnvVars(JadxArgs jadxArgs) {
Set<JadxSecurityFlag> flags = JadxSecurityFlag.all();
boolean modified = false;
boolean disableXmlSecurity = JadxCommonEnv.getBool("JADX_DISABLE_XML_SECURITY", false);
if (disableXmlSecurity) {
flags.remove(JadxSecurityFlag.SECURE_XML_PARSER);
// TODO: not related to 'xml security', but kept for compatibility
flags.remove(JadxSecurityFlag.VERIFY_APP_PACKAGE);
modified = true;
}
// TODO: migrate 'ZipSecurity'
if (modified) {
jadxArgs.setSecurity(new JadxSecurity(flags));
}
return 0;
}
private static boolean checkForErrors(JadxDecompiler jadx) {
@@ -87,6 +126,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);
+245 -70
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,18 @@ 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.UseSourceNameAsClassNameAlias;
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 +82,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 +100,18 @@ 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-restore-switch-over-string", description = "don't restore switch over string")
protected boolean restoreSwitchOverString = true;
@Parameter(names = "--no-replace-consts", description = "don't replace constant value with matching constant field")
protected boolean replaceConsts = true;
@@ -97,6 +121,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,27 +147,58 @@ 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;
protected GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.getDefault();
@Parameter(names = { "--deobf-use-sourcename" }, description = "use source file name as class name alias")
protected boolean deobfuscationUseSourceNameAsAlias = false;
@SuppressWarnings("DeprecatedIsStillUsed")
@Parameter(
names = { "--deobf-use-sourcename" },
description = "use source file name as class name alias."
+ "\nDEPRECATED, use \"--use-source-name-as-class-name-alias\" instead",
hidden = true
)
@Deprecated
protected Boolean deobfuscationUseSourceNameAsAlias = null;
@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-source-name-as-class-name-alias" },
description = "use source name as class name alias:"
+ "\n 'always' - always use source name if it's available"
+ "\n 'if-better' - use source name if it seems better than the current one"
+ "\n 'never' - never use source name, even if it's available",
converter = UseSourceNameAsClassNameConverter.class
)
protected UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias = null;
@Parameter(
names = { "--use-kotlin-methods-for-var-names" },
@@ -148,6 +219,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;
@@ -173,7 +254,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;
@@ -183,6 +264,9 @@ public class JadxCLIArgs {
@Parameter(names = { "-q", "--quiet" }, description = "turn off output (set --log-level to QUIET)")
protected boolean quiet = false;
@Parameter(names = { "--disable-plugins" }, description = "comma separated list of plugin ids to disable")
protected String disablePlugins = "";
@Parameter(names = { "--version" }, description = "print jadx version")
protected boolean printVersion = false;
@@ -215,6 +299,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;
@@ -223,14 +311,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;
}
@@ -254,27 +341,39 @@ public class JadxCLIArgs {
args.setCfgOutput(cfgOutput);
args.setRawCFGOutput(rawCfgOutput);
args.setReplaceConsts(replaceConsts);
if (userRenamesMappingsPath != null) {
args.setUserRenamesMappingsPath(userRenamesMappingsPath);
}
args.setUserRenamesMappingsMode(userRenamesMappingsMode);
args.setDeobfuscationOn(deobfuscationOn);
args.setDeobfuscationMapFile(FileUtils.toFile(deobfuscationMapFile));
args.setDeobfuscationMapFileMode(deobfuscationMapFileMode);
args.setGeneratedRenamesMappingFile(FileUtils.toFile(generatedRenamesMappingFile));
args.setGeneratedRenamesMappingFileMode(generatedRenamesMappingFileMode);
args.setDeobfuscationMinLength(deobfuscationMinLength);
args.setDeobfuscationMaxLength(deobfuscationMaxLength);
args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias);
args.setParseKotlinMetadata(deobfuscationParseKotlinMetadata);
args.setDeobfuscationWhitelist(Arrays.asList(deobfuscationWhitelistStr.split(" ")));
args.setUseSourceNameAsClassNameAlias(getUseSourceNameAsClassNameAlias());
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.setRestoreSwitchOverString(restoreSwitchOverString);
args.setRenameFlags(renameFlags);
args.setFsCaseSensitive(fsCaseSensitive);
args.setCommentsLevel(commentsLevel);
args.setIntegerFormat(integerFormat);
args.setUseDxInput(useDx);
args.setPluginOptions(pluginOptions);
args.setDisabledPlugins(Arrays.stream(disablePlugins.split(",")).map(String::trim).collect(Collectors.toSet()));
return args;
}
@@ -350,6 +449,30 @@ public class JadxCLIArgs {
return inlineMethods;
}
public boolean isMoveInnerClasses() {
return moveInnerClasses;
}
public boolean isAllowInlineKotlinLambda() {
return allowInlineKotlinLambda;
}
public boolean isExtractFinally() {
return extractFinally;
}
public boolean isRestoreSwitchOverString() {
return restoreSwitchOverString;
}
public Path getUserRenamesMappingsPath() {
return userRenamesMappingsPath;
}
public UserRenamesMappingsMode getUserRenamesMappingsMode() {
return userRenamesMappingsMode;
}
public boolean isDeobfuscationOn() {
return deobfuscationOn;
}
@@ -362,26 +485,49 @@ 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 GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {
return generatedRenamesMappingFileMode;
}
public UseSourceNameAsClassNameAlias getUseSourceNameAsClassNameAlias() {
if (useSourceNameAsClassNameAlias != null) {
return useSourceNameAsClassNameAlias;
} else if (deobfuscationUseSourceNameAsAlias != null) {
// noinspection deprecation
return UseSourceNameAsClassNameAlias.create(deobfuscationUseSourceNameAsAlias);
} else {
return UseSourceNameAsClassNameAlias.getDefault();
}
}
/**
* @deprecated Use {@link #getUseSourceNameAsClassNameAlias()} instead.
*/
@Deprecated
public boolean isDeobfuscationUseSourceNameAsAlias() {
return deobfuscationUseSourceNameAsAlias;
return getUseSourceNameAsClassNameAlias().toBoolean();
}
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;
}
@@ -406,6 +552,10 @@ public class JadxCLIArgs {
return exportAsGradleProject;
}
public boolean isSkipXmlPrettyPrint() {
return skipXmlPrettyPrint;
}
public boolean isRenameCaseSensitive() {
return renameFlags.contains(RenameEnum.CASE);
}
@@ -434,6 +584,10 @@ public class JadxCLIArgs {
return pluginOptions;
}
public String getDisablePlugins() {
return disablePlugins;
}
static class RenameConverter implements IStringConverter<Set<RenameEnum>> {
private final String paramName;
@@ -453,8 +607,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()));
}
@@ -463,54 +617,70 @@ 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 UseSourceNameAsClassNameConverter extends BaseEnumConverter<UseSourceNameAsClassNameAlias> {
public UseSourceNameAsClassNameConverter() {
super(UseSourceNameAsClassNameAlias::valueOf, UseSourceNameAsClassNameAlias::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()));
}
}
}
@@ -520,4 +690,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.LinkedHashMap;
import java.util.Map;
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 LinkedHashMap<>();
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,11 @@ 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.api.args.UseSourceNameAsClassNameAlias;
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 +24,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.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.NEVER);
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,204 @@
package jadx.cli.commands;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import jadx.api.plugins.JadxPluginInfo;
import jadx.cli.JCommanderWrapper;
import jadx.cli.LogHelper;
import jadx.core.utils.StringUtils;
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", defaultValueDescription = "<locationId>")
protected String install;
@Parameter(names = { "-j", "--install-jar" }, description = "install plugin from jar file", defaultValueDescription = "<path-to.jar>")
protected String installJar;
@Parameter(names = { "-l", "--list" }, description = "list installed plugins")
protected boolean list;
@Parameter(names = { "-a", "--available" }, description = "list available plugins from jadx-plugins-list (aka marketplace)")
protected boolean available;
@Parameter(names = { "-u", "--update" }, description = "update installed plugins")
protected boolean update;
@Parameter(names = { "--uninstall" }, description = "uninstall plugin with pluginId", defaultValueDescription = "<pluginId>")
protected String uninstall;
@Parameter(names = { "--disable" }, description = "disable plugin with pluginId", defaultValueDescription = "<pluginId>")
protected String disable;
@Parameter(names = { "--enable" }, description = "enable plugin with pluginId", defaultValueDescription = "<pluginId>")
protected String enable;
@Parameter(names = { "--list-all" }, description = "list all plugins including bundled and dropins")
protected boolean listAll;
@Parameter(
names = { "--list-versions" },
description = "fetch latest versions of plugin from locationId (will download all artefacts, limited to 10)",
defaultValueDescription = "<locationId>"
)
protected String listVersions;
@Parameter(names = { "-h", "--help" }, description = "print this help", help = true)
protected boolean printHelp = false;
@Override
public String name() {
return "plugins";
}
@SuppressWarnings("UnnecessaryReturnStatement")
@Override
public void process(JCommanderWrapper<?> jcw, JCommander subCommander) {
if (printHelp) {
jcw.printUsage(subCommander);
return;
}
Set<String> unknownOptions = new HashSet<>(subCommander.getUnknownOptions());
boolean verbose = unknownOptions.remove("-v") || unknownOptions.remove("--verbose");
LogHelper.setLogLevel(verbose ? LogHelper.LogLevelEnum.DEBUG : LogHelper.LogLevelEnum.INFO);
if (!unknownOptions.isEmpty()) {
System.out.println("Error: found unknown options: " + unknownOptions);
}
if (install != null) {
installPlugin(install);
return;
}
if (installJar != null) {
installPlugin("file:" + installJar);
return;
}
if (uninstall != null) {
boolean uninstalled = JadxPluginsTools.getInstance().uninstall(uninstall);
System.out.println(uninstalled ? "Uninstalled" : "Plugin not found");
return;
}
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());
}
}
return;
}
if (list) {
printPlugins(JadxPluginsTools.getInstance().getInstalled());
return;
}
if (listAll) {
printAllPlugins();
return;
}
if (listVersions != null) {
printVersions(listVersions, 10);
return;
}
if (available) {
List<JadxPluginMetadata> availableList = JadxPluginsList.getInstance().get();
System.out.println("Available plugins: " + availableList.size());
for (JadxPluginMetadata plugin : availableList) {
System.out.println(" - " + plugin.getName() + ": " + plugin.getDescription()
+ " (" + plugin.getLocationId() + ")");
}
return;
}
if (disable != null) {
if (JadxPluginsTools.getInstance().changeDisabledStatus(disable, true)) {
System.out.println("Plugin '" + disable + "' disabled.");
} else {
System.out.println("Plugin '" + disable + "' already disabled.");
}
return;
}
if (enable != null) {
if (JadxPluginsTools.getInstance().changeDisabledStatus(enable, false)) {
System.out.println("Plugin '" + enable + "' enabled.");
} else {
System.out.println("Plugin '" + enable + "' already enabled.");
}
return;
}
}
private static void printPlugins(List<JadxPluginMetadata> installed) {
System.out.println("Installed plugins: " + installed.size());
for (JadxPluginMetadata plugin : installed) {
StringBuilder sb = new StringBuilder();
sb.append(" - ").append(plugin.getPluginId());
String version = plugin.getVersion();
if (version != null) {
sb.append(" (").append(version).append(')');
}
if (plugin.isDisabled()) {
sb.append(" (disabled)");
}
sb.append(" - ").append(plugin.getName());
sb.append(": ").append(plugin.getDescription());
System.out.println(sb);
}
}
private void printVersions(String locationId, int limit) {
System.out.println("Loading ...");
List<JadxPluginMetadata> versions = JadxPluginsTools.getInstance().getVersionsByLocation(locationId, 1, limit);
if (versions.isEmpty()) {
System.out.println("No versions found");
return;
}
JadxPluginMetadata plugin = versions.get(0);
System.out.println("Versions for plugin id: " + plugin.getPluginId());
for (JadxPluginMetadata version : versions) {
StringBuilder sb = new StringBuilder();
sb.append(" - ").append(version.getVersion());
String reqVer = version.getRequiredJadxVersion();
if (StringUtils.notBlank(reqVer)) {
sb.append(", require jadx: ").append(reqVer);
}
System.out.println(sb);
}
}
private static void printAllPlugins() {
List<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();
printPlugins(installed);
Set<String> installedSet = installed.stream().map(JadxPluginMetadata::getPluginId).collect(Collectors.toSet());
List<JadxPluginInfo> plugins = JadxPluginsTools.getInstance().getAllPluginsInfo();
System.out.println("Other plugins: " + plugins.size());
for (JadxPluginInfo plugin : plugins) {
if (!installedSet.contains(plugin.getPluginId())) {
System.out.println(" - " + plugin.getPluginId()
+ " - " + plugin.getName()
+ ": " + plugin.getDescription());
}
}
}
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);
}
@@ -0,0 +1,31 @@
package jadx.cli.plugins;
import java.nio.file.Path;
import jadx.commons.app.JadxCommonFiles;
import jadx.commons.app.JadxTempFiles;
import jadx.core.plugins.files.IJadxFilesGetter;
public class JadxFilesGetter implements IJadxFilesGetter {
public static final JadxFilesGetter INSTANCE = new JadxFilesGetter();
@Override
public Path getConfigDir() {
return JadxCommonFiles.getConfigDir();
}
@Override
public Path getCacheDir() {
return JadxCommonFiles.getCacheDir();
}
@Override
public Path getTempDir() {
return JadxTempFiles.getTempRootDir();
}
private JadxFilesGetter() {
// singleton
}
}
@@ -12,7 +12,6 @@ import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,7 +19,10 @@ import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.android.TextResMapFile;
import jadx.core.xmlgen.ResTableParser;
import jadx.core.utils.files.ZipFile;
import jadx.core.xmlgen.ResTableBinaryParser;
import static jadx.core.utils.files.FileUtils.expandDirs;
/**
* Utility class for convert '.arsc' to simple text file with mapping id to resource name
@@ -30,7 +32,7 @@ public class ConvertArscFile {
private static int rewritesCount;
public static void usage() {
LOG.info("<res-map file> <input .arsc files>");
LOG.info("<res-map file> <input .arsc/android.jar files or dir>");
LOG.info("");
LOG.info("Note: If res-map already exists - it will be merged and updated");
}
@@ -42,6 +44,7 @@ public class ConvertArscFile {
}
List<Path> inputPaths = Stream.of(args).map(Paths::get).collect(Collectors.toList());
Path resMapFile = inputPaths.remove(0);
List<Path> inputResFiles = filterAndSort(expandDirs(inputPaths));
Map<Integer, String> resMap;
if (Files.isReadable(resMapFile)) {
resMap = TextResMapFile.read(resMapFile);
@@ -52,9 +55,8 @@ public class ConvertArscFile {
RootNode root = new RootNode(new JadxArgs()); // not really needed
rewritesCount = 0;
for (Path resFile : inputPaths) {
LOG.info("Processing {}", resFile);
ResTableParser resTableParser = new ResTableParser(root, true);
for (Path resFile : inputResFiles) {
ResTableBinaryParser resTableParser = new ResTableBinaryParser(root, true);
if (resFile.getFileName().toString().endsWith(".jar")) {
// Load resources.arsc from android.jar
try (ZipFile zip = new ZipFile(resFile.toFile())) {
@@ -84,6 +86,16 @@ public class ConvertArscFile {
LOG.info("done");
}
private static List<Path> filterAndSort(List<Path> inputPaths) {
return inputPaths.stream()
.filter(p -> {
String fileName = p.getFileName().toString();
return fileName.endsWith(".arsc") || fileName.endsWith(".jar");
})
.sorted()
.collect(Collectors.toList());
}
private static void mergeResMaps(Map<Integer, String> mainResMap, Map<Integer, String> newResMap) {
for (Map.Entry<Integer, String> entry : newResMap.entrySet()) {
Integer id = entry.getKey();
+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>
@@ -3,14 +3,12 @@ package jadx.cli;
import java.util.Collections;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static jadx.core.utils.Utils.newConstStringMap;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.assertj.core.api.Assertions.assertThat;
public class JadxCLIArgsTest {
@@ -18,38 +16,38 @@ public class JadxCLIArgsTest {
@Test
public void testInvertedBooleanOption() {
assertThat(parse("--no-replace-consts").isReplaceConsts(), is(false));
assertThat(parse("").isReplaceConsts(), is(true));
assertThat(parse("--no-replace-consts").isReplaceConsts()).isFalse();
assertThat(parse("").isReplaceConsts()).isTrue();
}
@Test
public void testEscapeUnicodeOption() {
assertThat(parse("--escape-unicode").isEscapeUnicode(), is(true));
assertThat(parse("").isEscapeUnicode(), is(false));
assertThat(parse("--escape-unicode").isEscapeUnicode()).isTrue();
assertThat(parse("").isEscapeUnicode()).isFalse();
}
@Test
public void testSrcOption() {
assertThat(parse("--no-src").isSkipSources(), is(true));
assertThat(parse("-s").isSkipSources(), is(true));
assertThat(parse("").isSkipSources(), is(false));
assertThat(parse("--no-src").isSkipSources()).isTrue();
assertThat(parse("-s").isSkipSources()).isTrue();
assertThat(parse("").isSkipSources()).isFalse();
}
@Test
public void testOptionsOverride() {
assertThat(override(new JadxCLIArgs(), "--no-imports").isUseImports(), is(false));
assertThat(override(new JadxCLIArgs(), "--no-debug-info").isDebugInfo(), is(false));
assertThat(override(new JadxCLIArgs(), "").isUseImports(), is(true));
assertThat(override(new JadxCLIArgs(), "--no-imports").isUseImports()).isFalse();
assertThat(override(new JadxCLIArgs(), "--no-debug-info").isDebugInfo()).isFalse();
assertThat(override(new JadxCLIArgs(), "").isUseImports()).isTrue();
JadxCLIArgs args = new JadxCLIArgs();
args.useImports = false;
assertThat(override(args, "--no-imports").isUseImports(), is(false));
assertThat(override(args, "--no-imports").isUseImports()).isFalse();
args.debugInfo = false;
assertThat(override(args, "--no-debug-info").isDebugInfo(), is(false));
assertThat(override(args, "--no-debug-info").isDebugInfo()).isFalse();
args = new JadxCLIArgs();
args.useImports = false;
assertThat(override(args, "").isUseImports(), is(false));
assertThat(override(args, "").isUseImports()).isFalse();
}
@Test
@@ -83,7 +81,7 @@ public class JadxCLIArgsTest {
JadxCLIArgs args = new JadxCLIArgs();
args.pluginOptions = baseMap;
Map<String, String> resultMap = override(args, providedArgs).getPluginOptions();
assertThat(resultMap, Matchers.equalTo(expectedMap));
assertThat(resultMap).isEqualTo(expectedMap);
}
private JadxCLIArgs parse(String... args) {
@@ -91,15 +89,15 @@ public class JadxCLIArgsTest {
}
private JadxCLIArgs parse(JadxCLIArgs jadxArgs, String... args) {
boolean res = jadxArgs.processArgs(args);
assertThat(res, is(true));
LOG.info("Jadx args: {}", jadxArgs.toJadxArgs());
return jadxArgs;
return check(jadxArgs, jadxArgs.processArgs(args));
}
private JadxCLIArgs override(JadxCLIArgs jadxArgs, String... args) {
boolean res = jadxArgs.overrideProvided(args);
assertThat(res, is(true));
return check(jadxArgs, jadxArgs.overrideProvided(args));
}
private static JadxCLIArgs check(JadxCLIArgs jadxArgs, boolean res) {
assertThat(res).isTrue();
LOG.info("Jadx args: {}", jadxArgs.toJadxArgs());
return jadxArgs;
}
@@ -7,10 +7,10 @@ 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.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class RenameConverterTest {
@@ -24,25 +24,24 @@ public class RenameConverterTest {
@Test
public void all() {
Set<RenameEnum> set = converter.convert("all");
assertEquals(3, set.size());
assertTrue(set.contains(RenameEnum.CASE));
assertTrue(set.contains(RenameEnum.VALID));
assertTrue(set.contains(RenameEnum.PRINTABLE));
assertThat(set).hasSize(3);
assertThat(set).contains(RenameEnum.CASE);
assertThat(set).contains(RenameEnum.VALID);
assertThat(set).contains(RenameEnum.PRINTABLE);
}
@Test
public void none() {
Set<RenameEnum> set = converter.convert("none");
assertTrue(set.isEmpty());
assertThat(set).isEmpty();
}
@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");
assertEquals("'wrong' is unknown for parameter someParam, possible values are case, valid, printable",
thrown.getMessage());
assertThat(thrown.getMessage()).isEqualTo("'wrong' is unknown for parameter someParam, possible values are case, valid, printable");
}
}
+87 -52
View File
@@ -12,71 +12,111 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.AfterAll;
import org.assertj.core.api.Condition;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.utils.files.FileUtils;
import static org.assertj.core.api.Assertions.assertThat;
public class TestInput {
private static final Logger LOG = LoggerFactory.getLogger(TestInput.class);
private static final PathMatcher LOG_ALL_FILES = path -> {
LOG.debug("File in result dir: {}", path);
return true;
};
@TempDir
Path testDir;
@Test
public void testHelp() {
int result = JadxCLI.execute(new String[] { "--help" });
assertThat(result).isEqualTo(0);
}
@Test
public void testApkInput() throws Exception {
int result = JadxCLI.execute(buildArgs(List.of(), "samples/small.apk"));
assertThat(result).isEqualTo(0);
List<Path> resultFiles = collectAllFilesInDir(testDir);
printFiles(resultFiles);
assertThat(resultFiles)
.describedAs("check output files")
.map(p -> p.getFileName().toString())
.haveExactly(2, new Condition<>(f -> f.endsWith(".java"), "java classes"))
.haveExactly(9, new Condition<>(f -> f.endsWith(".xml"), "xml resources"))
.haveExactly(1, new Condition<>(f -> f.equals("classes.dex"), "dex"))
.haveExactly(1, new Condition<>(f -> f.equals("AndroidManifest.xml"), "manifest"))
.hasSize(13);
}
@Test
public void testDexInput() throws Exception {
decompile("dex", "samples/hello.dex");
decompile("samples/hello.dex");
}
@Test
public void testSmaliInput() throws Exception {
decompile("smali", "samples/HelloWorld.smali");
decompile("samples/HelloWorld.smali");
}
@Test
public void testClassInput() throws Exception {
decompile("class", "samples/HelloWorld.class");
decompile("samples/HelloWorld.class");
}
@Test
public void testMultipleInput() throws Exception {
decompile("multi", "samples/hello.dex", "samples/HelloWorld.smali");
decompile("samples/hello.dex", "samples/HelloWorld.smali");
}
@Test
public void testFallbackMode() throws Exception {
int result = JadxCLI.execute(buildArgs(List.of("-f"), "samples/hello.dex"));
assertThat(result).isEqualTo(0);
List<Path> files = collectJavaFilesInDir(testDir);
assertThat(files).hasSize(1);
}
@Test
public void testSimpleMode() throws Exception {
int result = JadxCLI.execute(buildArgs(List.of("--decompilation-mode", "simple"), "samples/hello.dex"));
assertThat(result).isEqualTo(0);
List<Path> files = collectJavaFilesInDir(testDir);
assertThat(files).hasSize(1);
}
@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]));
int result = JadxCLI.execute(buildArgs(List.of(), "samples/resources-only.apk"));
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();
List<Path> files = collectFilesInDir(testDir,
path -> path.getFileName().toString().equalsIgnoreCase("AndroidManifest.xml"));
assertThat(files).isNotEmpty();
}
private void decompile(String tmpDirName, String... inputSamples) throws URISyntaxException, IOException {
List<String> args = new ArrayList<>();
Path tempDir = FileUtils.createTempDir(tmpDirName);
private void decompile(String... inputSamples) throws URISyntaxException, IOException {
int result = JadxCLI.execute(buildArgs(List.of(), inputSamples));
assertThat(result).isEqualTo(0);
List<Path> resultJavaFiles = collectJavaFilesInDir(testDir);
assertThat(resultJavaFiles).isNotEmpty();
// do not copy input files as resources
for (Path path : collectFilesInDir(testDir, LOG_ALL_FILES)) {
for (String inputSample : inputSamples) {
assertThat(path.toAbsolutePath().toString()).doesNotContain(inputSample);
}
}
}
private String[] buildArgs(List<String> options, String... inputSamples) throws URISyntaxException {
List<String> args = new ArrayList<>(options);
args.add("-v");
args.add("-d");
args.add(tempDir.toAbsolutePath().toString());
args.add(testDir.toAbsolutePath().toString());
for (String inputSample : inputSamples) {
URL resource = getClass().getClassLoader().getResource(inputSample);
@@ -84,21 +124,13 @@ public class TestInput {
String sampleFile = resource.toURI().getRawPath();
args.add(sampleFile);
}
return args.toArray(new String[0]);
}
int result = JadxCLI.execute(args.toArray(new String[0]));
assertThat(result).isEqualTo(0);
List<Path> resultJavaFiles = collectJavaFilesInDir(tempDir);
assertThat(resultJavaFiles).isNotEmpty();
// do not copy input files as resources
PathMatcher logAllFiles = path -> {
LOG.debug("File in result dir: {}", path);
return true;
};
for (Path path : collectFilesInDir(tempDir, logAllFiles)) {
for (String inputSample : inputSamples) {
assertThat(path.toAbsolutePath().toString()).doesNotContain(inputSample);
}
private void printFiles(List<Path> files) {
LOG.info("Output files (count: {}):", files.size());
for (Path file : files) {
LOG.info(" {}", testDir.relativize(file));
}
}
@@ -107,6 +139,14 @@ public class TestInput {
return collectFilesInDir(dir, javaMatcher);
}
private static List<Path> collectAllFilesInDir(Path dir) throws IOException {
try (Stream<Path> pathStream = Files.walk(dir)) {
return pathStream
.filter(Files::isRegularFile)
.collect(Collectors.toList());
}
}
private static List<Path> collectFilesInDir(Path dir, PathMatcher matcher) throws IOException {
try (Stream<Path> pathStream = Files.walk(dir)) {
return pathStream
@@ -115,9 +155,4 @@ public class TestInput {
.collect(Collectors.toList());
}
}
@AfterAll
public static void cleanup() {
FileUtils.clearTempRootDir();
}
}
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
## jadx app commons
This module contains common utilities used in jadx apps (cli and gui) and not needed in jadx-code module:
- `JadxCommonFiles` - wrapper for `dev.dirs:directories` lib to get
'config' and 'cache' directories in cross-platform way
- `JadxCommonEnv` - utils for work with environment variables
@@ -0,0 +1,7 @@
plugins {
id("jadx-library")
}
dependencies {
implementation("dev.dirs:directories:26")
}
@@ -0,0 +1,29 @@
package jadx.commons.app;
public class JadxCommonEnv {
public static String get(String varName, String defValue) {
String strValue = System.getenv(varName);
return isNullOrEmpty(strValue) ? defValue : strValue;
}
public static boolean getBool(String varName, boolean defValue) {
String strValue = System.getenv(varName);
if (isNullOrEmpty(strValue)) {
return defValue;
}
return strValue.equalsIgnoreCase("true");
}
public static int getInt(String varName, int defValue) {
String strValue = System.getenv(varName);
if (isNullOrEmpty(strValue)) {
return defValue;
}
return Integer.parseInt(strValue);
}
private static boolean isNullOrEmpty(String value) {
return value == null || value.isEmpty();
}
}
@@ -0,0 +1,74 @@
package jadx.commons.app;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
import dev.dirs.ProjectDirectories;
public class JadxCommonFiles {
private static final Path CONFIG_DIR;
private static final Path CACHE_DIR;
public static Path getConfigDir() {
return CONFIG_DIR;
}
public static Path getCacheDir() {
return CACHE_DIR;
}
static {
DirsLoader loader = new DirsLoader();
loader.init();
CONFIG_DIR = loader.getConfigDir();
CACHE_DIR = loader.getCacheDir();
}
private static final class DirsLoader {
private @Nullable ProjectDirectories dirs;
private Path configDir;
private Path cacheDir;
public void init() {
try {
configDir = loadEnvDir("JADX_CONFIG_DIR", pd -> pd.configDir);
cacheDir = loadEnvDir("JADX_CACHE_DIR", pd -> pd.cacheDir);
} catch (Exception e) {
throw new RuntimeException("Failed to init common directories", e);
}
}
private Path loadEnvDir(String envVar, Function<ProjectDirectories, String> dirFunc) throws IOException {
String envDir = JadxCommonEnv.get(envVar, null);
String dirStr;
if (envDir != null) {
dirStr = envDir;
} else {
dirStr = dirFunc.apply(loadDirs());
}
Path path = Path.of(dirStr).toAbsolutePath();
Files.createDirectories(path);
return path;
}
private synchronized ProjectDirectories loadDirs() {
if (dirs == null) {
dirs = ProjectDirectories.from("io.github", "skylot", "jadx");
}
return dirs;
}
public Path getCacheDir() {
return cacheDir;
}
public Path getConfigDir() {
return configDir;
}
}
}
@@ -0,0 +1,31 @@
package jadx.commons.app;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class JadxTempFiles {
private static final String JADX_TMP_INSTANCE_PREFIX = "jadx-instance-";
private static final Path TEMP_ROOT_DIR = createTempRootDir();
public static Path getTempRootDir() {
return TEMP_ROOT_DIR;
}
private static Path createTempRootDir() {
try {
String jadxTmpDir = System.getenv("JADX_TMP_DIR");
Path dir;
if (jadxTmpDir != null) {
dir = Files.createTempDirectory(Paths.get(jadxTmpDir), JADX_TMP_INSTANCE_PREFIX);
} else {
dir = Files.createTempDirectory(JADX_TMP_INSTANCE_PREFIX);
}
dir.toFile().deleteOnExit();
return dir;
} catch (Exception e) {
throw new RuntimeException("Failed to create temp root directory", e);
}
}
}
-28
View File
@@ -1,28 +0,0 @@
plugins {
id 'jadx-library'
}
dependencies {
api(project(':jadx-plugins:jadx-plugins-api'))
implementation 'com.google.code.gson:gson:2.9.0'
// TODO: move resources decoding to separate plugin module
implementation 'com.android.tools.build:aapt2-proto:7.2.1-7984345'
implementation 'com.google.protobuf:protobuf-java:3.21.2' // forcing latest version
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.30.0'
testImplementation 'tools.profiler:async-profiler:1.8.3'
}
test {
exclude '**/tmp/*'
}
+54
View File
@@ -0,0 +1,54 @@
plugins {
id("jadx-library")
}
dependencies {
api(project(":jadx-plugins:jadx-input-api"))
implementation("com.google.code.gson:gson:2.11.0")
testImplementation("org.apache.commons:commons-lang3:3.17.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")
}
val jadxTestJavaVersion = getTestJavaVersion()
fun getTestJavaVersion(): Int? {
val envVarName = "JADX_TEST_JAVA_VERSION"
val testJavaVer = System.getenv(envVarName)?.toInt() ?: return null
val currentJavaVer = java.toolchain.languageVersion.get().asInt()
if (testJavaVer < currentJavaVer) {
throw GradleException("'$envVarName' can't be set to lower version than $currentJavaVer")
}
println("Set Java toolchain for core tests to version '$testJavaVer'")
return testJavaVer
}
tasks.named<Test>("test") {
jadxTestJavaVersion?.let { testJavaVer ->
javaLauncher =
javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(testJavaVer)
}
}
// disable cache to allow test's rerun,
// because most tests are integration and depends on plugins and environment
outputs.cacheIf { false }
// exclude temp tests
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();
+319 -41
View File
@@ -1,31 +1,57 @@
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;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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.UseSourceNameAsClassNameAlias;
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.security.IJadxSecurity;
import jadx.api.security.JadxSecurityFlag;
import jadx.api.security.impl.JadxSecurity;
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.plugins.files.IJadxFilesGetter;
import jadx.core.plugins.files.TempFilesGetter;
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 +63,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 +85,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 +101,43 @@ public class JadxArgs {
*/
private boolean includeDependencies = false;
private boolean deobfuscationOn = false;
private boolean useSourceNameAsClassAlias = false;
private boolean parseKotlinMetadata = false;
private File deobfuscationMapFile = null;
private Path userRenamesMappingsPath = null;
private UserRenamesMappingsMode userRenamesMappingsMode = UserRenamesMappingsMode.getDefault();
private DeobfuscationMapFileMode deobfuscationMapFileMode = DeobfuscationMapFileMode.READ;
private boolean deobfuscationOn = false;
private UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias = UseSourceNameAsClassNameAlias.getDefault();
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 restoreSwitchOverString = true;
private boolean skipXmlPrettyPrint = false;
private boolean fsCaseSensitive;
public enum RenameEnum {
@@ -99,8 +156,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 {
@@ -109,13 +172,35 @@ public class JadxArgs {
private UseKotlinMethodsForVarNames useKotlinMethodsForVarNames = UseKotlinMethodsForVarNames.APPLY;
/**
* Additional files structure info.
* Defaults to tmp dirs.
*/
private IJadxFilesGetter filesGetter = TempFilesGetter.INSTANCE;
/**
* Additional data validation and security checks
*/
private IJadxSecurity security = new JadxSecurity(JadxSecurityFlag.all());
/**
* Don't save files (can be using for performance testing)
*/
private boolean skipFilesSave = false;
/**
* Run additional expensive checks to verify internal invariants and info integrity
*/
private boolean runDebugChecks = false;
private Map<String, String> pluginOptions = new HashMap<>();
private Set<String> disabledPlugins = new HashSet<>();
private JadxPluginLoader pluginLoader = new JadxBasePluginLoader();
private boolean loadJadxClsSetFile = true;
public JadxArgs() {
// use default options
}
@@ -126,16 +211,24 @@ public class JadxArgs {
setOutDirRes(new File(rootDir, DEFAULT_RES_DIR));
}
@Override
public void close() {
try {
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 +354,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 +410,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,40 +434,47 @@ 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 UseSourceNameAsClassNameAlias getUseSourceNameAsClassNameAlias() {
return useSourceNameAsClassNameAlias;
}
public void setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias) {
this.useSourceNameAsClassNameAlias = useSourceNameAsClassNameAlias;
}
/**
* @deprecated Use {@link #getUseSourceNameAsClassNameAlias()} instead.
*/
@Deprecated
public boolean isUseSourceNameAsClassAlias() {
return useSourceNameAsClassAlias;
return getUseSourceNameAsClassNameAlias().toBoolean();
}
/**
* @deprecated Use {@link #setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias)} instead.
*/
@Deprecated
public void setUseSourceNameAsClassAlias(boolean useSourceNameAsClassAlias) {
this.useSourceNameAsClassAlias = useSourceNameAsClassAlias;
}
public boolean isParseKotlinMetadata() {
return parseKotlinMetadata;
}
public void setParseKotlinMetadata(boolean parseKotlinMetadata) {
this.parseKotlinMetadata = parseKotlinMetadata;
final var useSourceNameAsClassNameAlias = UseSourceNameAsClassNameAlias.create(useSourceNameAsClassAlias);
setUseSourceNameAsClassNameAlias(useSourceNameAsClassNameAlias);
}
public int getDeobfuscationMinLength() {
@@ -361,12 +493,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 +565,22 @@ public class JadxArgs {
this.exportAsGradleProject = exportAsGradleProject;
}
public boolean isRestoreSwitchOverString() {
return restoreSwitchOverString;
}
public void setRestoreSwitchOverString(boolean restoreSwitchOverString) {
this.restoreSwitchOverString = restoreSwitchOverString;
}
public boolean isSkipXmlPrettyPrint() {
return skipXmlPrettyPrint;
}
public void setSkipXmlPrettyPrint(boolean skipXmlPrettyPrint) {
this.skipXmlPrettyPrint = skipXmlPrettyPrint;
}
public boolean isFsCaseSensitive() {
return fsCaseSensitive;
}
@@ -485,6 +665,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 +681,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 +705,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;
}
@@ -517,6 +729,22 @@ public class JadxArgs {
this.useKotlinMethodsForVarNames = useKotlinMethodsForVarNames;
}
public IJadxFilesGetter getFilesGetter() {
return filesGetter;
}
public void setFilesGetter(IJadxFilesGetter filesGetter) {
this.filesGetter = filesGetter;
}
public IJadxSecurity getSecurity() {
return security;
}
public void setSecurity(IJadxSecurity security) {
this.security = security;
}
public boolean isSkipFilesSave() {
return skipFilesSave;
}
@@ -525,6 +753,14 @@ public class JadxArgs {
this.skipFilesSave = skipFilesSave;
}
public boolean isRunDebugChecks() {
return runDebugChecks;
}
public void setRunDebugChecks(boolean runDebugChecks) {
this.runDebugChecks = runDebugChecks;
}
public Map<String, String> getPluginOptions() {
return pluginOptions;
}
@@ -533,19 +769,56 @@ public class JadxArgs {
this.pluginOptions = pluginOptions;
}
public Set<String> getDisabledPlugins() {
return disabledPlugins;
}
public void setDisabledPlugins(Set<String> disabledPlugins) {
this.disabledPlugins = disabledPlugins;
}
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
+ useSourceNameAsClassNameAlias
+ resourceNameSource
+ useKotlinMethodsForVarNames
+ insertDebugLines + extractFinally
+ debugInfo + useSourceNameAsClassAlias + escapeUnicode + replaceConsts
+ debugInfo + escapeUnicode + replaceConsts + restoreSwitchOverString
+ 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 +834,25 @@ public class JadxArgs {
+ ", skipResources=" + skipResources
+ ", skipSources=" + skipSources
+ ", includeDependencies=" + includeDependencies
+ ", userRenamesMappingsPath=" + userRenamesMappingsPath
+ ", userRenamesMappingsMode=" + userRenamesMappingsMode
+ ", deobfuscationOn=" + deobfuscationOn
+ ", deobfuscationMapFile=" + deobfuscationMapFile
+ ", deobfuscationMapFileMode=" + deobfuscationMapFileMode
+ ", useSourceNameAsClassAlias=" + useSourceNameAsClassAlias
+ ", parseKotlinMetadata=" + parseKotlinMetadata
+ ", generatedRenamesMappingFile=" + generatedRenamesMappingFile
+ ", generatedRenamesMappingFileMode=" + generatedRenamesMappingFileMode
+ ", resourceNameSource=" + resourceNameSource
+ ", useSourceNameAsClassNameAlias=" + useSourceNameAsClassNameAlias
+ ", useKotlinMethodsForVarNames=" + useKotlinMethodsForVarNames
+ ", insertDebugLines=" + insertDebugLines
+ ", extractFinally=" + extractFinally
+ ", deobfuscationMinLength=" + deobfuscationMinLength
+ ", deobfuscationMaxLength=" + deobfuscationMaxLength
+ ", deobfuscationWhitelist=" + deobfuscationWhitelist
+ ", escapeUnicode=" + escapeUnicode
+ ", replaceConsts=" + replaceConsts
+ ", restoreSwitchOverString=" + restoreSwitchOverString
+ ", 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,26 +25,33 @@ 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.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.xmlgen.BinaryXMLParser;
import jadx.core.xmlgen.ProtoXMLParser;
import jadx.core.xmlgen.ResContainer;
import jadx.core.utils.files.ZipPatch;
import jadx.core.utils.tasks.TaskExecutor;
import jadx.core.xmlgen.ResourcesSaver;
/**
@@ -83,89 +85,103 @@ 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;
private final List<ICodeLoader> loadedInputs = new ArrayList<>();
private RootNode root;
private List<JavaClass> classes;
private List<ResourceFile> resources;
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 ResourcesLoader resourcesLoader;
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<>();
private IJadxEvents events = new JadxEventsImpl();
public JadxDecompiler() {
this(new JadxArgs());
}
public JadxDecompiler(JadxArgs args) {
this.args = args;
this.args = Objects.requireNonNull(args);
this.pluginManager = new JadxPluginManager(this);
this.resourcesLoader = new ResourcesLoader(this);
}
public void load() {
reset();
JadxArgsValidator.validate(this);
LOG.info("loading ...");
loadPlugins(args);
FileUtils.updateTempRootDir(args.getFilesGetter().getTempDir());
loadPlugins();
loadInputFiles();
root = new RootNode(args);
root.init();
root.setDecompilerRef(this);
root.mergePasses(customPasses);
root.loadClasses(loadedInputs);
root.initClassPath();
root.loadResources(getResources());
root.loadResources(resourcesLoader, 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() {
loadedInputs.clear();
List<Path> inputPaths = Utils.collectionMap(args.getInputFiles(), File::toPath);
List<File> inputs = ZipPatch.patchZipFiles(args.getInputFiles());
args.setInputFiles(inputs);
List<Path> inputPaths = Utils.collectionMap(inputs, 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();
FileUtils.clearTempRootDir();
}
private void closeInputs() {
@@ -179,23 +195,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);
}
}
}
@@ -219,13 +249,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) {
@@ -242,97 +271,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)) {
@@ -344,27 +398,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 {
ClassNode clsNode = cls.getClassNode();
ICodeInfo code = clsNode.getCode();
SaveCode.save(outDir, clsNode, code);
} catch (Exception e) {
LOG.error("Error saving class: {}", cls, e);
}
}
});
list.add(cls);
}
return list;
}
public List<JavaClass> getClasses() {
@@ -391,36 +427,18 @@ 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();
}
resources = new ResourcesLoader(this).load();
resources = resourcesLoader.load(root);
}
return resources;
}
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() {
@@ -453,51 +471,60 @@ public final class JadxDecompiler implements Closeable {
return root;
}
synchronized BinaryXMLParser getBinaryXmlParser() {
if (binaryXmlParser == null) {
binaryXmlParser = new BinaryXMLParser(root);
}
return binaryXmlParser;
}
synchronized ProtoXMLParser getProtoXmlParser() {
if (protoXmlParser == null) {
protoXmlParser = new ProtoXMLParser(root);
}
return protoXmlParser;
}
/**
* 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 parentCls = mthNode.getParentClass();
return new JavaMethod(convertClassNode(parentCls), 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;
}
@ApiStatus.Internal
synchronized JavaPackage convertPackageNode(PackageNode pkg) {
JavaPackage foundPkg = pkg.getJavaNode();
if (foundPkg != null) {
return foundPkg;
}
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));
}
}
pkg.setJavaNode(javaPkg);
return javaPkg;
}
@Nullable
@@ -574,14 +601,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
@@ -643,6 +665,41 @@ public final class JadxDecompiler implements Closeable {
return decompileScheduler;
}
public IJadxEvents events() {
return events;
}
public void setEventsImpl(IJadxEvents eventsImpl) {
this.events = eventsImpl;
}
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);
}
public ResourcesLoader getResourcesLoader() {
return resourcesLoader;
}
@Override
public String toString() {
return "jadx decompiler " + getVersion();
@@ -99,6 +99,11 @@ public final class JavaClass implements JavaNode {
return false;
}
@Override
public ICodeNodeRef getCodeNodeRef() {
return cls;
}
/**
* Internal API. Not Stable!
*/
@@ -247,6 +252,10 @@ public final class JavaClass implements JavaNode {
return cls.getPackage();
}
public JavaPackage getJavaPackage() {
return cls.getPackageNode().getJavaNode();
}
@Override
public JavaClass getDeclaringClass() {
return parent;
@@ -329,7 +338,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!
*/
@@ -2,7 +2,6 @@ package jadx.api;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.jetbrains.annotations.ApiStatus;
@@ -10,6 +9,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;
@@ -78,15 +78,9 @@ public final class JavaMethod implements JavaNode {
return Collections.emptyList();
}
JadxDecompiler decompiler = getDeclaringClass().getRootDecompiler();
return ovrdAttr.getRelatedMthNodes().stream()
.map(m -> {
JavaMethod javaMth = decompiler.convertMethodNode(m);
if (javaMth == null) {
LOG.warn("Failed convert to java method: {}", m);
}
return javaMth;
})
.filter(Objects::nonNull)
return ovrdAttr.getRelatedMthNodes()
.stream()
.map(decompiler::convertMethodNode)
.collect(Collectors.toList());
}
@@ -103,6 +97,10 @@ public final class JavaMethod implements JavaNode {
return mth.getDefPosition();
}
public String getCodeStr() {
return mth.getCodeStr();
}
@Override
public void removeAlias() {
this.mth.getMethodInfo().removeAlias();
@@ -116,6 +114,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,107 @@
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);
}
public boolean isDescendantOf(JavaPackage ancestor) {
JavaPackage current = this;
while (current != null) {
if (ancestor.equals(current)) {
return true;
}
if (current.getPkgNode().getParentPkg() == null) {
current = null;
} else {
current = current.getPkgNode().getParentPkg().getJavaNode();
}
}
return false;
}
@Override
public ICodeNodeRef getCodeNodeRef() {
return pkgNode;
}
@Internal
public PackageNode getPkgNode() {
return pkgNode;
}
@Override
public JavaClass getDeclaringClass() {
return null;
@@ -48,7 +119,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 +138,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 +150,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();
}
}
@@ -7,6 +7,7 @@ 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;
@@ -37,6 +38,11 @@ public class JavaVariable implements JavaNode {
return varNode.getName();
}
@Override
public ICodeNodeRef getCodeNodeRef() {
return varNode;
}
@ApiStatus.Internal
public VarNode getVarNode() {
return varNode;
@@ -71,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() {
@@ -9,37 +9,50 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.ResourceFile.ZipRef;
import jadx.api.impl.SimpleCodeInfo;
import jadx.api.plugins.CustomResourcesLoader;
import jadx.api.plugins.resources.IResContainerFactory;
import jadx.api.plugins.resources.IResTableParserProvider;
import jadx.api.plugins.resources.IResourcesLoader;
import jadx.api.plugins.utils.ZipSecurity;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.Utils;
import jadx.core.utils.android.Res9patchStreamDecoder;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.core.utils.files.ZipFile;
import jadx.core.xmlgen.BinaryXMLParser;
import jadx.core.xmlgen.IResTableParser;
import jadx.core.xmlgen.ResContainer;
import jadx.core.xmlgen.ResProtoParser;
import jadx.core.xmlgen.ResTableParser;
import jadx.core.xmlgen.ResTableBinaryParserProvider;
import static jadx.core.utils.files.FileUtils.READ_BUFFER_SIZE;
import static jadx.core.utils.files.FileUtils.copyStream;
// TODO: move to core package
public final class ResourcesLoader {
public final class ResourcesLoader implements IResourcesLoader {
private static final Logger LOG = LoggerFactory.getLogger(ResourcesLoader.class);
private final JadxDecompiler jadxRef;
private final List<IResTableParserProvider> resTableParserProviders = new ArrayList<>();
private final List<IResContainerFactory> resContainerFactories = new ArrayList<>();
private BinaryXMLParser binaryXmlParser;
ResourcesLoader(JadxDecompiler jadxRef) {
this.jadxRef = jadxRef;
this.resTableParserProviders.add(new ResTableBinaryParserProvider());
}
List<ResourceFile> load() {
List<ResourceFile> load(RootNode root) {
init(root);
List<File> inputFiles = jadxRef.getArgs().getInputFiles();
List<ResourceFile> list = new ArrayList<>(inputFiles.size());
for (File file : inputFiles) {
@@ -48,10 +61,37 @@ public final class ResourcesLoader {
return list;
}
private void init(RootNode root) {
for (IResTableParserProvider resTableParserProvider : resTableParserProviders) {
try {
resTableParserProvider.init(root);
} catch (Exception e) {
throw new JadxRuntimeException("Failed to init res table provider: " + resTableParserProvider);
}
}
for (IResContainerFactory resContainerFactory : resContainerFactories) {
try {
resContainerFactory.init(root);
} catch (Exception e) {
throw new JadxRuntimeException("Failed to init res container factory: " + resContainerFactory);
}
}
}
public interface ResourceDecoder<T> {
T decode(long size, InputStream is) throws IOException;
}
@Override
public void addResContainerFactory(IResContainerFactory resContainerFactory) {
resContainerFactories.add(resContainerFactory);
}
@Override
public void addResTableParserProvider(IResTableParserProvider resTableParserProvider) {
resTableParserProviders.add(resTableParserProvider);
}
public static <T> T decodeStream(ResourceFile rf, ResourceDecoder<T> decoder) throws JadxException {
try {
ZipRef zipRef = rf.getZipRef();
@@ -81,7 +121,8 @@ public final class ResourcesLoader {
static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf) {
try {
return decodeStream(rf, (size, is) -> loadContent(jadxRef, rf, is));
ResourcesLoader resLoader = jadxRef.getResourcesLoader();
return decodeStream(rf, (size, is) -> resLoader.loadContent(rf, is));
} catch (JadxException e) {
LOG.error("Decode error", e);
ICodeWriter cw = jadxRef.getRoot().makeCodeWriter();
@@ -91,36 +132,48 @@ public final class ResourcesLoader {
}
}
private static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf,
InputStream inputStream) throws IOException {
RootNode root = jadxRef.getRoot();
switch (rf.getType()) {
case MANIFEST:
case XML: {
ICodeInfo content;
if (root.isProto()) {
content = jadxRef.getProtoXmlParser().parse(inputStream);
} else {
content = jadxRef.getBinaryXmlParser().parse(inputStream);
}
return ResContainer.textResource(rf.getDeobfName(), content);
private ResContainer loadContent(ResourceFile resFile, InputStream inputStream) throws IOException {
for (IResContainerFactory customFactory : resContainerFactories) {
ResContainer resContainer = customFactory.create(resFile, inputStream);
if (resContainer != null) {
return resContainer;
}
}
switch (resFile.getType()) {
case MANIFEST:
case XML:
ICodeInfo content = loadBinaryXmlParser().parse(inputStream);
return ResContainer.textResource(resFile.getDeobfName(), content);
case ARSC:
if (root.isProto()) {
return new ResProtoParser(root).decodeFiles(inputStream);
} else {
return new ResTableParser(root).decodeFiles(inputStream);
}
return decodeTable(resFile, inputStream).decodeFiles();
case IMG:
return decodeImage(rf, inputStream);
return decodeImage(resFile, inputStream);
default:
return ResContainer.resourceFileLink(rf);
return ResContainer.resourceFileLink(resFile);
}
}
public IResTableParser decodeTable(ResourceFile resFile, InputStream is) throws IOException {
if (resFile.getType() != ResourceType.ARSC) {
throw new IllegalArgumentException("Unexpected resource type for decode: " + resFile.getType() + ", expect '.pb'/'.arsc'");
}
IResTableParser parser = null;
for (IResTableParserProvider provider : resTableParserProviders) {
parser = provider.getParser(resFile);
if (parser != null) {
break;
}
}
if (parser == null) {
throw new JadxRuntimeException("Unknown type of resource file: " + resFile.getOriginalName());
}
parser.decode(is);
return parser;
}
private static ResContainer decodeImage(ResourceFile rf, InputStream inputStream) {
String name = rf.getOriginalName();
if (name.endsWith(".9.png")) {
@@ -140,9 +193,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 +218,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);
@@ -169,4 +236,11 @@ public final class ResourcesLoader {
copyStream(is, baos);
return new SimpleCodeInfo(baos.toString("UTF-8"));
}
private synchronized BinaryXMLParser loadBinaryXmlParser() {
if (binaryXmlParser == null) {
binaryXmlParser = new BinaryXMLParser(jadxRef.getRoot());
}
return binaryXmlParser;
}
}
@@ -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,38 @@
package jadx.api.args;
import jadx.core.utils.exceptions.JadxRuntimeException;
public enum UseSourceNameAsClassNameAlias {
ALWAYS,
IF_BETTER,
NEVER;
public static UseSourceNameAsClassNameAlias getDefault() {
return NEVER;
}
/**
* @deprecated Use {@link UseSourceNameAsClassNameAlias} directly.
*/
@Deprecated
public boolean toBoolean() {
switch (this) {
case IF_BETTER:
return true;
case NEVER:
return false;
case ALWAYS:
throw new JadxRuntimeException("No match between " + this + " and boolean");
default:
throw new JadxRuntimeException("Unhandled strategy: " + this);
}
}
/**
* @deprecated Use {@link UseSourceNameAsClassNameAlias} directly.
*/
@Deprecated
public static UseSourceNameAsClassNameAlias create(boolean useSourceNameAsAlias) {
return useSourceNameAsAlias ? IF_BETTER : NEVER;
}
}
@@ -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);
@@ -65,7 +62,7 @@ public class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter
buf.append(cw.getCodeStr());
return this;
}
AnnotatedCodeWriter code = ((AnnotatedCodeWriter) cw);
AnnotatedCodeWriter code = (AnnotatedCodeWriter) cw;
line--;
int startPos = getLength();
for (Map.Entry<Integer, ICodeAnnotation> entry : code.annotations.entrySet()) {
@@ -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,6 +6,7 @@ public interface ICodeAnnotation {
CLASS,
FIELD,
METHOD,
PKG,
VAR,
VAR_REF,
DECLARATION,

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