Compare commits
933 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 03009b5159 | |||
| d25eda4839 | |||
| 59003bdb1f | |||
| 9d4babcdce | |||
| 8c28a8530e | |||
| 6775cc5a93 | |||
| e648e60af9 | |||
| e6b7db35c1 | |||
| c3f7027bdd | |||
| 2fe95da570 | |||
| 62fa2735dc | |||
| bce6611aaf | |||
| 21aa90c5d1 | |||
| 55e79fb70f | |||
| 044c75ab9f | |||
| 97fa8ff210 | |||
| 27c283fb11 | |||
| bd1c3fffde | |||
| 169ad2901f | |||
| 869422b424 | |||
| ccc4164d54 | |||
| b61642a646 | |||
| 189e4181de | |||
| 5b960db77e | |||
| 3119e8c893 | |||
| f95c5e8f39 | |||
| 1e908f7af3 | |||
| 7ce40baacb | |||
| 7d689a85ea | |||
| c7a162d827 | |||
| 325b3ac991 | |||
| 9a8a11619b | |||
| 7ae6bd737c | |||
| 57fd9b5bdb | |||
| cdd5bf536d | |||
| dcce3aaa39 | |||
| b3d86ae908 | |||
| 8b7d3f497e | |||
| 462e582bb8 | |||
| 14a7b63707 | |||
| 4051a50146 | |||
| b3db337abd | |||
| 15ea9a56b9 | |||
| 165ae24722 | |||
| 11b38ffed2 | |||
| 81dfac6d83 | |||
| a3bd3b09fd | |||
| 13d306024a | |||
| ff64da705c | |||
| 00196e412b | |||
| 06c4fea4d2 | |||
| 69fd88d883 | |||
| f2f145019d | |||
| 7b3563fb62 | |||
| 22ee9a216c | |||
| 06b4467fdc | |||
| ff778ab372 | |||
| ff4dde62ae | |||
| 468b52342d | |||
| 09d39de604 | |||
| eb079bd435 | |||
| d1b1fc0ab6 | |||
| 859d479569 | |||
| b0954e9620 | |||
| dfe1d0477d | |||
| a1aa6d7ecd | |||
| 6f01e9f76b | |||
| be8b96280e | |||
| 2a2806ebd7 | |||
| c7a0f7a092 | |||
| 9710ebe09a | |||
| f0da486703 | |||
| 925503ba04 | |||
| 039900a278 | |||
| d73455c689 | |||
| 07b3e5a7c0 | |||
| c1fc73a524 | |||
| 0d982e9709 | |||
| e6b38e172a | |||
| 331c4aaa5e | |||
| 4e82233cbc | |||
| ad267e1618 | |||
| 54265e34e5 | |||
| c677901aa5 | |||
| 220a2198a1 | |||
| b725dd18b6 | |||
| a0466d4494 | |||
| ae1a5e9277 | |||
| 018c20187d | |||
| add11dff1d | |||
| 005a59668c | |||
| 8f727325d6 | |||
| 7bbb58863b | |||
| 8629e4cf22 | |||
| bd5c6b6516 | |||
| 85a7aaa9fb | |||
| a3df83e60f | |||
| b3be2794a0 | |||
| 02ea3be8f2 | |||
| b78745a87b | |||
| 01af6481f6 | |||
| 0c3e6e77cd | |||
| 8b48219dc3 | |||
| 80187c7e29 | |||
| 22f7b40151 | |||
| 3e709d6693 | |||
| 1aa16c4664 | |||
| 9fba709687 | |||
| 6b61599114 | |||
| 2829e284f3 | |||
| 27b15aeb1c | |||
| 74c52a8884 | |||
| 39e7b82353 | |||
| 365e258180 | |||
| ea68024851 | |||
| 56ae4a6048 | |||
| 1d831d82d4 | |||
| 4c760f1029 | |||
| cf0101f13d | |||
| 748f45b386 | |||
| 9e278efc5c | |||
| cc1beab0b5 | |||
| 2fba204b33 | |||
| 9bf079aad4 | |||
| 6aeaf6aca9 | |||
| 7ea478e18a | |||
| ef99412de1 | |||
| cda1b1ad2c | |||
| 05863881f8 | |||
| 3e208509e0 | |||
| bd75baef56 | |||
| d191f62b8d | |||
| f17c46224d | |||
| e008e818b0 | |||
| 560b1a9ca7 | |||
| 06f113f0a6 | |||
| 48a0073002 | |||
| 88e90722ae | |||
| 1becfa9977 | |||
| 20ed13936a | |||
| 421de675a2 | |||
| 06a118c104 | |||
| 2e93656007 | |||
| c8c4adb60c | |||
| f2e78fe800 | |||
| 7172f8df2d | |||
| d7c6be5664 | |||
| 654cf5e4fb | |||
| bf2d5b5e2e | |||
| 0f495afc99 | |||
| 5f1985f281 | |||
| 2a574d45d5 | |||
| 3ee476c6b6 | |||
| fd3d488016 | |||
| 9898cbb4a1 | |||
| 104a0f0636 | |||
| e58b77267e | |||
| 73913651b4 | |||
| f01e6aa505 | |||
| d9da6a7f89 | |||
| 5726a52ab6 | |||
| f61d90ec2f | |||
| 2cd112cd3d | |||
| 43358643be | |||
| 1f0d3dac0f | |||
| da95a8ae17 | |||
| da3ac6bff0 | |||
| bdbeaff8f0 | |||
| b1f48f1db1 | |||
| 5b09378614 | |||
| b64c93160b | |||
| 58c4f56a71 | |||
| 3d11d1fa87 | |||
| 0d158592e4 | |||
| 0c253f9a1f | |||
| f565178c8c | |||
| f6d13f1860 | |||
| d58c9ac926 | |||
| 7f9d51b9b1 | |||
| 432e49df03 | |||
| b530c234f3 | |||
| 181dcf7b4f | |||
| dc4dcb2bd0 | |||
| 74c396448e | |||
| cb9693a9d1 | |||
| 5d13acc6f3 | |||
| c04dddfa81 | |||
| 1bb645d676 | |||
| ecb597a461 | |||
| 46cd3b5597 | |||
| d523f1b15e | |||
| 8030c2f84e | |||
| 47224dc599 | |||
| d492628bfe | |||
| 13e934ce4d | |||
| 7b54c3ae70 | |||
| b82791706a | |||
| e51a7fe417 | |||
| cf96fdec59 | |||
| 3374f9b64a | |||
| 6b54cde89c | |||
| 59b560b553 | |||
| 8b08ac3806 | |||
| d9d4180581 | |||
| 33f93ddc8a | |||
| bcd0c949dc | |||
| fc0f1f9a1c | |||
| abd64007e2 | |||
| fb02e32a6a | |||
| f33a2e4768 | |||
| 3d8e5e5851 | |||
| 646ee2d963 | |||
| b6f27d8a1a | |||
| 97d04edb01 | |||
| 2486c981a8 | |||
| b7a8a2879e | |||
| 092e897104 | |||
| a0a9f7fd41 | |||
| 00f0f5547b | |||
| 00608f8e51 | |||
| d0351a88ba | |||
| bee476895c | |||
| 580f25faae | |||
| 1d1ca7d0c0 | |||
| dbf4527ce6 | |||
| ec726d6ca1 | |||
| be1c02455f | |||
| aee1e86398 | |||
| c0d721bea1 | |||
| e19a456642 | |||
| acd57930cc | |||
| 73348e5183 | |||
| 32c92dd9a8 | |||
| 8bbdbfc563 | |||
| fd6cb2451b | |||
| 47647bbb9a | |||
| e31d697cd9 | |||
| a796d15289 | |||
| f56eb271a1 | |||
| fbebcb9845 | |||
| 79c91634ad | |||
| f6e12d71a0 | |||
| 62835fbade | |||
| 07c66b5c3c | |||
| e3aa49aaa9 | |||
| 9981949a2b | |||
| ea6492e5ba | |||
| 03d4cb134f | |||
| 37b0b09f25 | |||
| 1e75544636 | |||
| d4ce969fb7 | |||
| 9a692d6be4 | |||
| d3a8a43c74 | |||
| 518da3d8b5 | |||
| 61f5386fe5 | |||
| 20cb9c6a3b | |||
| 4a6784dc68 | |||
| bb6db25c9d | |||
| d0c496858e | |||
| b0ab702f9e | |||
| 5846e6d22e | |||
| 4daad2fc79 | |||
| cca6ca25d1 | |||
| dfa6a83f7c | |||
| d1a3935c9e | |||
| b4f1021885 | |||
| a163e5a1de | |||
| 6eeb303d73 | |||
| 3209dbb7a4 | |||
| d84f0389ec | |||
| 5d720dd29c | |||
| c9d650d186 | |||
| ce60aa8635 | |||
| 4a9276e904 | |||
| b78d3aa2f7 | |||
| 4644d1d8ac | |||
| 7b8fc01319 | |||
| ff66f95a8a | |||
| 4ef1f3b12b | |||
| 8873038b57 | |||
| bf58f03405 | |||
| acf1c8187e | |||
| 7bd1b14728 | |||
| 61f04d6b07 | |||
| 5d5bf325fe | |||
| afdd2d8b39 | |||
| b18604071a | |||
| 801890f0a8 | |||
| 3d36c93beb | |||
| 54fbbd9524 | |||
| 306547d499 | |||
| a43b475be7 | |||
| bc4bb0dc41 | |||
| ea5916452d | |||
| 19f3cdf501 | |||
| 45d320a596 | |||
| 6860a8be7e | |||
| 94915db739 | |||
| 29d114402d | |||
| 6889670b11 | |||
| fe7d527fcc | |||
| 84e211b809 | |||
| f4849d67cf | |||
| 60a8d8b9fd | |||
| 1449354c54 | |||
| 0df474f35a | |||
| de629544a3 | |||
| 7a2dad8ef2 | |||
| a46e35c15c | |||
| 73966fda89 | |||
| fe0ab5ebf8 | |||
| 8345edf76b | |||
| ff0fbba720 | |||
| fe41d6ed3d | |||
| ff95b9e999 | |||
| 87844d2193 | |||
| 2ac0cc62e6 | |||
| 2924bb259c | |||
| 17695babf4 | |||
| 0d105a5095 | |||
| 7eab3c8534 | |||
| 47f2e516e5 | |||
| fdad829c49 | |||
| 2fa7f84251 | |||
| 828cad3287 | |||
| cc0cb6a3d3 | |||
| a67dfcb7e1 | |||
| baa93bad63 | |||
| 45df80f036 | |||
| 792e0d6f3a | |||
| fe9d3bcab7 | |||
| 1e1036c049 | |||
| 60dcdc7096 | |||
| c4c3d42d16 | |||
| 58c36de8c2 | |||
| b4ca566a19 | |||
| e644bad758 | |||
| dd86abcc5e | |||
| f0513a1a55 | |||
| be6cb573b1 | |||
| 5d064d3e50 | |||
| 15b6309e2c | |||
| 417bb7a7e9 | |||
| 4c74e8e300 | |||
| 57238de6ff | |||
| 313c4a121a | |||
| 39912398fc | |||
| 7544d1a113 | |||
| cfbe5ab672 | |||
| 2661b91a6f | |||
| 61578e8793 | |||
| cc6a893402 | |||
| 4d8a5d6671 | |||
| 982307b1ac | |||
| 37054dc84e | |||
| 343e2c531a | |||
| 6fa5d247f0 | |||
| 688dea0c50 | |||
| c0815b12bc | |||
| 233f8692b2 | |||
| e5be41b9cc | |||
| 3788e4ef3a | |||
| 32855f4511 | |||
| 3d5e225274 | |||
| 8a34d973ff | |||
| a43b3282ef | |||
| 249801880c | |||
| 742d30d770 | |||
| 267413332a | |||
| f9c94f27f1 | |||
| d2131d9640 | |||
| 548821c4df | |||
| 958ab245ae | |||
| 8f3cc3e8c1 | |||
| b872ffd1b9 | |||
| c5263f92fe | |||
| 1475e887c8 | |||
| c21cabcba7 | |||
| 063af8cd62 | |||
| 2d10537050 | |||
| 3814951408 | |||
| 964bd62d35 | |||
| 3ed2df828f | |||
| b26abdc851 | |||
| 90185fd947 | |||
| 681f8a98b5 | |||
| 0b225238fb | |||
| a7649dda7a | |||
| b5e3dcf70f | |||
| 7abbc81886 | |||
| 9c30aeacdb | |||
| 8f27de4d0e | |||
| 02b69d2d29 | |||
| e6fde48b69 | |||
| 109dea0857 | |||
| 1d34328dd3 | |||
| ef4f1d3ed4 | |||
| 23696d3971 | |||
| efa2f5d172 | |||
| 699ceb197e | |||
| 5c83c22501 | |||
| 7bb5c0a859 | |||
| 603863403f | |||
| 889a945cf1 | |||
| fd80e03809 | |||
| b5807082d9 | |||
| 5d1f0b8cae | |||
| 3f9aa34057 | |||
| e2c860f260 | |||
| 0938351d97 | |||
| 937dd20794 | |||
| ea5e87560a | |||
| 5fbbf2150e | |||
| 0e11bffe82 | |||
| ba9af5c288 | |||
| cca706c94f | |||
| 2df69bbfb4 | |||
| f5307636ef | |||
| 9a39b70a46 | |||
| e63808bc4b | |||
| 847225a6a9 | |||
| eee354a3ab | |||
| 1cc00a54f3 | |||
| ffdad1b652 | |||
| 9a8ec76989 | |||
| 0be5b2cea9 | |||
| c94201be4a | |||
| 1051dacb1e | |||
| a2bfe9bbe8 | |||
| 8c6ec3bccc | |||
| 015876b790 | |||
| 280f3870a9 | |||
| e9d770ae9e | |||
| 5f1bd1d9ba | |||
| 60fb458024 | |||
| 1b08779536 | |||
| c37e39a819 | |||
| 2d58fbd4b1 | |||
| ffbf800404 | |||
| 500aa8a68d | |||
| 58e8268126 | |||
| f9da6e00ed | |||
| 821cc668c7 | |||
| 287ba49008 | |||
| 115e563a2b | |||
| 1669200e62 | |||
| 6ab224ea0d | |||
| 61855a7ea1 | |||
| bda3119e86 | |||
| b26abed686 | |||
| c179beee95 | |||
| 04a454094b | |||
| 33bcbfec41 | |||
| ec645d80b1 | |||
| a8d889de3d | |||
| ec0bf701c8 | |||
| ad4dd116be | |||
| ef79f266c8 | |||
| 366225f9be | |||
| 730db0d24f | |||
| 05fb77e9bd | |||
| bbabfa0354 | |||
| 96bd9f0f17 | |||
| fd5b397b40 | |||
| f5e3a261b4 | |||
| 52a884608a | |||
| 74ddfde950 | |||
| 9aacb4f312 | |||
| 82e2104f3c | |||
| 09fa35f144 | |||
| f2a6a1e942 | |||
| b85900aa3d | |||
| 37a42d1418 | |||
| 07dde05337 | |||
| 8618214c7f | |||
| b80f32a36f | |||
| ce527ed753 | |||
| f2ea6415c9 | |||
| bc70f8eabb | |||
| be25cbf8c2 | |||
| f9c0cad146 | |||
| b356ff76e1 | |||
| ec9244a635 | |||
| a5bd64461d | |||
| 54bf79ccc5 | |||
| 6182332eef | |||
| 37b57096ec | |||
| 6aab8fabc9 | |||
| 665c1e57d2 | |||
| 6e8affcbdc | |||
| 41d6b0018e | |||
| dbadbb01fc | |||
| 0f52077c5c | |||
| ea861829c7 | |||
| c1de235289 | |||
| 8f969d4e89 | |||
| 0c1f830f94 | |||
| 43c082e4da | |||
| ecdc4e6757 | |||
| b865c9c687 | |||
| 6b4976c593 | |||
| 2807dc5090 | |||
| 463d2b90fa | |||
| bff00d101f | |||
| 1290ef63a2 | |||
| 49d2b34d84 | |||
| eecdfae73f | |||
| 8760b4ddde | |||
| 3599b248a4 | |||
| 2fdd496518 | |||
| 278e3c2d47 | |||
| 881a716b8e | |||
| a73c9e90fc | |||
| 56749b2afb | |||
| d7ec35791b | |||
| d51362ed50 | |||
| 5c0c1daa71 | |||
| 603ea3989a | |||
| 018ff98df7 | |||
| 5fbabdefca | |||
| 13607fc8b6 | |||
| 0c33d723c8 | |||
| 0143423dc9 | |||
| 21b1452485 | |||
| ecb8abb98e | |||
| a3a4fabd5a | |||
| edf6ce273c | |||
| 1bb956a8b0 | |||
| 276ee537e1 | |||
| 7e628ad1a1 | |||
| 0a40b53412 | |||
| 682f90de2f | |||
| 1a07ab8ab1 | |||
| d86449a8ea | |||
| 75d2e540aa | |||
| e73612b4d2 | |||
| a19aec9d9f | |||
| 61be1d8b0a | |||
| 4483533417 | |||
| fb703cd856 | |||
| c0ff7572ac | |||
| 8e7ffc8ddb | |||
| f994abee21 | |||
| 6f1dfb6848 | |||
| 306bc7abc3 | |||
| 8ed48183c7 | |||
| fdc3fe1a8d | |||
| 2805770510 | |||
| 03052baabe | |||
| 23e643c686 | |||
| faeae086d1 | |||
| 38e64fafbd | |||
| e723c245ee | |||
| 3e57dacfd3 | |||
| f5accc8464 | |||
| 238fe17df0 | |||
| 295befbaaa | |||
| 5f20033f18 | |||
| 5f376c6e21 | |||
| 68a99c5410 | |||
| 8db70ee7a5 | |||
| b6155afd32 | |||
| d5bf9f20a6 | |||
| 2d5c0fda4a | |||
| 5d56001826 | |||
| 2de91f9a3a | |||
| e06e6bd483 | |||
| 1e57e85382 | |||
| ca03406a3d | |||
| 2d28da9b0e | |||
| edb1717969 | |||
| 1b51234edc | |||
| e6d896d91e | |||
| a989fa7e64 | |||
| f7002c7fad | |||
| 41d986bdca | |||
| 192a8116f1 | |||
| 173d85bf84 | |||
| b310e92d7a | |||
| 4a92ee07cc | |||
| fdffe20c46 | |||
| 15d464d620 | |||
| 1bd4526e4c | |||
| 816308e3ca | |||
| cfd851a980 | |||
| d129be7e86 | |||
| 6433fcef72 | |||
| d120556772 | |||
| 19f40b8549 | |||
| 89acf73010 | |||
| b70276d896 | |||
| 76b370e249 | |||
| b73f2ef0b9 | |||
| 762c2d70bd | |||
| 4e81bdd76f | |||
| 4d87b447a6 | |||
| 8f0d0e4f4d | |||
| c39a696977 | |||
| b78a0257b4 | |||
| cdede7eca4 | |||
| 72180164d8 | |||
| 2dc0db230c | |||
| 24657f6b3c | |||
| e358476c71 | |||
| 45a637f33b | |||
| 76cf9596e2 | |||
| 95c07a4e76 | |||
| 2d1d5a419c | |||
| f45547e2c1 | |||
| 1f463c7152 | |||
| 2c92c6b0a6 | |||
| f83ee45ac8 | |||
| f695fafe74 | |||
| f6ab105ef7 | |||
| f81a2c4ebb | |||
| c602b3d967 | |||
| f213082da5 | |||
| 8d26fa2a89 | |||
| a06231d007 | |||
| 68b84ea786 | |||
| 868e99e0b5 | |||
| 95db98e574 | |||
| 63fc7e05b6 | |||
| 2c2bb64c09 | |||
| 0f5d07c6b1 | |||
| fbb6aa580e | |||
| d9692a4f5f | |||
| ef0c09ca5b | |||
| 752c61ae71 | |||
| 8f635076ea | |||
| 8d5130a329 | |||
| 09a5e0893b | |||
| de8c315a7a | |||
| 3abdd40498 | |||
| 5b7ebec7e3 | |||
| de603ef909 | |||
| a0a6779097 | |||
| ce9645864d | |||
| d076c4e73d | |||
| 3e4c6a9c51 | |||
| 74a2d7d5e7 | |||
| 219f9ebede | |||
| ba0e918fef | |||
| 9b75a4f199 | |||
| 2d65594383 | |||
| 36f439a82b | |||
| 0efc99e43e | |||
| c0c35f32a8 | |||
| 53e51c76a5 | |||
| 14265a7b70 | |||
| 4467f9118f | |||
| 240a903438 | |||
| afa331e8fc | |||
| 6620e650ef | |||
| 2aa7438346 | |||
| 26fb7f33fb | |||
| 6f4451364b | |||
| 1a642108ef | |||
| dddb7b2878 | |||
| 82cafe6e94 | |||
| b578479270 | |||
| 85b77cd3c0 | |||
| 38c3f8bf9a | |||
| f558203a9d | |||
| 347d6e2c2e | |||
| eb4d540b04 | |||
| 83b6ffbba9 | |||
| 50c5f0874f | |||
| a72e6aeafe | |||
| 683cd76cc5 | |||
| df313dbfec | |||
| 2e3579a8fd | |||
| 99c8ceae9a | |||
| fa67f4fef7 | |||
| e9a7ccd00c | |||
| 76e5824d99 | |||
| 3bf101d7ac | |||
| c28320e1b2 | |||
| ea5b66d463 | |||
| 8a67c39279 | |||
| 67054bda3d | |||
| ed4c5a3a17 | |||
| ae1b1ce99e | |||
| 5d3c96ead2 | |||
| 63ad7fb128 | |||
| ccdbb1d62c | |||
| 3474f0da04 | |||
| 0fd9a9df29 | |||
| e29011eb95 | |||
| f52d2814df | |||
| 6e507f0cdc | |||
| f825901849 | |||
| 5aaceee978 | |||
| f53dbbfebf | |||
| d29263ef57 | |||
| d8c8fb4e18 | |||
| 835f9fb5e0 | |||
| ecbcbbe8d4 | |||
| 37b1bff8f2 | |||
| 5e8f9b900f | |||
| ddefead764 | |||
| 54f83cee1c | |||
| 7f3bd8c668 | |||
| ea10f2af47 | |||
| 2d810a2864 | |||
| bac2386a1a | |||
| 06ec12ba17 | |||
| 7f713e5592 | |||
| ee2556ecb6 | |||
| e57787393e | |||
| b7634024d6 | |||
| 9417671606 | |||
| 2e652b4219 | |||
| 1d6ebed7e5 | |||
| 19cf22b9e3 | |||
| 75fbdd3ac4 | |||
| 13e317d927 | |||
| 7a309ca367 | |||
| a992c93198 | |||
| ee3a653c1b | |||
| e8e7028792 | |||
| 2403d32ae4 | |||
| 1c89c7290c | |||
| 1891f6fd7e | |||
| e933b41236 | |||
| 9f7432134c | |||
| 7d69e5aaab | |||
| 780b1a0d53 | |||
| ea37f3e9f7 | |||
| c6ed117df6 | |||
| cedcc29e01 | |||
| d5219e7f0c | |||
| 9a6dec0dbd | |||
| 17574ee554 | |||
| 926f4e497a | |||
| acbe94df27 | |||
| a2f018a00b | |||
| 98cc1b1022 | |||
| 745f46f81f | |||
| 6912ed40b4 | |||
| a89dbc1152 | |||
| 0c4d46ead5 | |||
| eae9bac938 | |||
| cb91c8c41c | |||
| cb1f3e9843 | |||
| d4927db52b | |||
| bc7300bd01 | |||
| 278d7fa3f9 | |||
| d9af91bc4d | |||
| 18fe9f305c | |||
| e5e64365fc | |||
| fdf170529f | |||
| 50283ab543 | |||
| 3fa3e5acec | |||
| 4230cd5b5a | |||
| 1ad6527de5 | |||
| 0421ad80c1 | |||
| 35e0201f06 | |||
| 118eea5e77 | |||
| 7f317be325 | |||
| e1aa9f6de4 | |||
| 058a5e3bb2 | |||
| 92b49ec2b5 | |||
| 583a04b092 | |||
| 444a04e2f7 | |||
| 157e702ffd | |||
| 77892f41ec | |||
| 6ba0e1dbf6 | |||
| 950fbbaa83 | |||
| 912c431511 | |||
| 5d6b82724a | |||
| 78c976ad4f | |||
| fbdfd135da | |||
| dd51783d9e | |||
| 158fc2fca3 | |||
| 24284a6f3a | |||
| 85c2c63aa3 | |||
| f354f7de63 | |||
| 540c0a8100 | |||
| 4d00fede56 | |||
| b1bc5c08ff | |||
| 305d4f4fe5 | |||
| 2d149e9a5d | |||
| 87b9ff3c35 | |||
| 1c36b3c74c | |||
| 068e4b8e3d | |||
| df38a6424f | |||
| 5d186e56a5 | |||
| 0fafcfa006 | |||
| e3fdbafd86 | |||
| 07c2b14479 | |||
| cdc844aaf3 | |||
| e1b7d361b9 | |||
| 12ef29bebc | |||
| 22ed241d50 | |||
| 28e5a3c5be | |||
| bb4d88cc68 | |||
| 4aaea2b93f | |||
| bc8d7c4fc3 | |||
| 5ea6c46778 | |||
| b28f8ba85b | |||
| 4db50fb749 | |||
| 1dd0c90a04 | |||
| 2bace2bde2 | |||
| 1a9cb832ab | |||
| 6844a46c93 | |||
| e9e45707da | |||
| b9d02ff4c4 | |||
| 29b64300bc | |||
| 777355e86e | |||
| 620a177ce8 | |||
| 683c2dfbeb | |||
| 266cbcc6f4 | |||
| 8a45602ae6 | |||
| 711419a797 | |||
| 603f3057eb | |||
| fa6fc1f871 | |||
| 49fa320989 | |||
| 2f301bf150 | |||
| b4892ce17f | |||
| 151c171616 | |||
| 79477a2de3 | |||
| 78aadda931 | |||
| b50706505f | |||
| 9114821fb1 | |||
| 1195582da8 | |||
| 258987b0ff | |||
| a6a734c70d | |||
| d6c23a2a9b | |||
| db028904d7 | |||
| 63a571306c | |||
| bc4db61e25 | |||
| c7e6e28830 | |||
| 1d7b6fdb2c | |||
| ce5d8eeff8 | |||
| 894e0e6132 | |||
| 127f0ecf3f | |||
| cf7767e702 | |||
| e0aedc7949 | |||
| bad78de74c | |||
| 12df8a169f | |||
| 15c9d33339 | |||
| 7e0fafbaf1 | |||
| 57b9c1dd7a | |||
| 8ba0c17259 | |||
| cd32151083 | |||
| 75b52d672e | |||
| 11d04508f7 | |||
| e641b773b5 | |||
| 6e5899c654 | |||
| c66ffaa7f9 | |||
| 5193c6a5d8 | |||
| e7212af547 | |||
| 3ca1357af4 | |||
| 90e95213e4 | |||
| ae2d4da585 | |||
| 691d5cd1e6 | |||
| 58a46c6417 | |||
| d3f6160e62 | |||
| 03e4afb12f | |||
| 6802f6028e | |||
| 5ca61cfe18 | |||
| 32d55b48f2 | |||
| ab4b6f9e54 | |||
| 9100ad1220 | |||
| 8b4f8fb572 | |||
| 87e0e5bf16 | |||
| e4c2d6cf6e | |||
| fb0bdb5112 | |||
| f4b3645435 | |||
| c27f2badf7 | |||
| 1a877d6535 | |||
| 5ada9331b6 | |||
| a0f4ccb7a4 | |||
| 5b5524a7dd | |||
| 3cc464c9c9 | |||
| 51555667cf | |||
| e01ea7010f | |||
| 77732c83c9 | |||
| a67fc83949 | |||
| 3d920725aa | |||
| 2f2fbea558 | |||
| e7a86a2960 | |||
| b282d97ffe | |||
| e4ca52a95f | |||
| d972d9ec74 | |||
| 0721a6b050 | |||
| 762ee6550e | |||
| 18070eb7a6 | |||
| 8486891728 | |||
| 4679172d4f | |||
| 92a6c333d8 | |||
| 358adbdd65 | |||
| 65f7c80222 | |||
| d2e6bb236e | |||
| eaeb114258 | |||
| 1533b7fe6e | |||
| a2cd8e1ead | |||
| 4edb512121 | |||
| 702b88228c | |||
| 14fd88b2f8 | |||
| 20657e8bb5 | |||
| 93d3194e3b | |||
| 39331d9120 | |||
| b4fa6644bc | |||
| 0b2e2ed034 | |||
| 81231206f3 | |||
| 49d0e76272 | |||
| 0809993b37 | |||
| 0c3afcc24c | |||
| d6c851eed4 | |||
| dcf4a7c4e3 | |||
| 9ba07b986b | |||
| e6b6b93cbb | |||
| fcd58ae76f | |||
| df380dea27 | |||
| 9d88592391 | |||
| c906c11b0f | |||
| 4fbc56cdb0 | |||
| 98c0416b20 | |||
| fa41874e30 | |||
| 2aa6c99c90 | |||
| 5f60c0f1bb | |||
| cb741db623 | |||
| 1df217c4a0 | |||
| 81f209ba9e | |||
| 34a31aa7df | |||
| 5099e02c9b | |||
| f364b39b29 | |||
| 4cd4746f9a | |||
| 6448f0e32b | |||
| e07332d49a | |||
| bd8a44c4c9 |
+13
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Set update schedule for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -0,0 +1,90 @@
|
||||
name: Build Artifacts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, build-test ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 25
|
||||
|
||||
- name: Set jadx version
|
||||
run: |
|
||||
JADX_REV=$(git rev-list --count HEAD)
|
||||
JADX_VERSION="r${JADX_REV}.${GITHUB_SHA:0:7}"
|
||||
echo "JADX_VERSION=$JADX_VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew dist distWin
|
||||
env:
|
||||
JADX_BUILD_JAVA_VERSION: 11
|
||||
|
||||
- name: Save bundle artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
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: 14
|
||||
|
||||
- name: Save Windows bundle artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
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: 14
|
||||
|
||||
build-win-bundle:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: oracle-actions/setup-java@v1
|
||||
with:
|
||||
release: 25
|
||||
|
||||
- name: Print Java version
|
||||
shell: bash
|
||||
run: java -version
|
||||
|
||||
- name: Set jadx version
|
||||
shell: bash
|
||||
run: |
|
||||
JADX_REV=$(git rev-list --count HEAD)
|
||||
JADX_VERSION="r${JADX_REV}.${GITHUB_SHA:0:7}"
|
||||
echo "JADX_VERSION=$JADX_VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew dist -PbundleJRE=true
|
||||
|
||||
- name: Save Windows with JRE bundle artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ format('jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}
|
||||
# Upload unpacked files for now
|
||||
path: jadx-gui/build/jadx-gui-with-jre-win/*
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Build Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, build-test ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 25
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew build dist distWin
|
||||
env:
|
||||
JADX_BUILD_JAVA_VERSION: 11
|
||||
@@ -1,52 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
|
||||
- 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}"
|
||||
echo "JADX_VERSION=$JADX_VERSION" >> $GITHUB_ENV
|
||||
|
||||
- uses: burrunan/gradle-cache-action@v1
|
||||
name: Build with Gradle
|
||||
env:
|
||||
TERM: dumb
|
||||
TEST_INPUT_PLUGIN: dx
|
||||
with:
|
||||
arguments: clean build dist copyExe --warning-mode=all
|
||||
|
||||
- name: Save bundle artifact
|
||||
if: success() && github.event_name == 'push'
|
||||
uses: actions/upload-artifact@v2
|
||||
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
|
||||
|
||||
- name: Save exe artifact
|
||||
if: success() && github.event_name == 'push'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ format('jadx-gui-{0}-no-jre-win.exe', env.JADX_VERSION) }}
|
||||
path: build/*.exe
|
||||
if-no-files-found: error
|
||||
@@ -1,41 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '0 9 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['java']
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
queries: +security-extended
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Don't build tests in jadx-core also skip tests execution and checkstyle tasks
|
||||
- run: |
|
||||
./gradlew clean build -x checkstyleTest -x checkstyleMain -x test -x ':jadx-core:testClasses'
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -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
|
||||
@@ -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@v6
|
||||
|
||||
- name: Set up JDK
|
||||
uses: oracle-actions/setup-java@v1
|
||||
with:
|
||||
release: 25
|
||||
|
||||
- name: Set jadx version
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const jadxVersion = context.ref.split('/').pop().substring(1)
|
||||
core.exportVariable('JADX_VERSION', jadxVersion);
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew dist -PbundleJRE=true
|
||||
|
||||
- name: Save JRE bundle artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
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@v6
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 25
|
||||
|
||||
- name: Set jadx version and release name
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const jadxVersion = context.ref.split('/').pop().substring(1)
|
||||
core.exportVariable('JADX_VERSION', jadxVersion);
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew dist distWin
|
||||
env:
|
||||
JADX_BUILD_JAVA_VERSION: 11
|
||||
|
||||
- name: Download Windows JRE bundle
|
||||
uses: actions/download-artifact@v8
|
||||
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@v3
|
||||
with:
|
||||
name: ${{ env.JADX_VERSION }}
|
||||
draft: true
|
||||
fail_on_unmatched_files: true
|
||||
files: build/jadx-*.zip
|
||||
+8
-1
@@ -21,19 +21,26 @@ build/
|
||||
classes/
|
||||
idea/
|
||||
.gradle/
|
||||
.kotlin/
|
||||
node_modules/
|
||||
.vscode/
|
||||
|
||||
jadx-output/
|
||||
*-tmp/
|
||||
**/tmp/
|
||||
*.jobf
|
||||
*.jadx
|
||||
|
||||
*.class
|
||||
*.jar
|
||||
*.dump
|
||||
*.log
|
||||
*.cfg
|
||||
*.orig
|
||||
quark.json
|
||||
*.json
|
||||
*.dot
|
||||
|
||||
.env
|
||||
|
||||
cliff.toml
|
||||
jadx-gui/src/main/resources/logback.xml
|
||||
|
||||
+3
-13
@@ -8,17 +8,7 @@ before_script:
|
||||
stages:
|
||||
- test
|
||||
|
||||
java-8:
|
||||
build-test:
|
||||
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
|
||||
|
||||
java-latest:
|
||||
stage: test
|
||||
image: openjdk:latest
|
||||
script: java -version && ./gradlew clean build dist --warning-mode=all
|
||||
image: eclipse-temurin:21
|
||||
script: JADX_BUILD_JAVA_VERSION=11 JADX_TEST_JAVA_VERSION=11 ./gradlew clean build dist distWin
|
||||
|
||||
@@ -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 --enable-native-access=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
@@ -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
@@ -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
@@ -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`
|
||||
|
||||
@@ -2,20 +2,25 @@
|
||||
|
||||
## JADX
|
||||
|
||||
[](https://github.com/skylot/jadx/actions?query=workflow%3ABuild)
|
||||
[](https://lgtm.com/projects/g/skylot/jadx/alerts/)
|
||||
[](https://github.com/semantic-release/semantic-release)
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
[](https://search.maven.org/search?q=g:io.github.skylot%20AND%20jadx)
|
||||

|
||||
[](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
|
||||
|
||||
@@ -33,8 +38,9 @@ See these features in action here: [jadx-gui features overview](https://github.c
|
||||
<img src="https://user-images.githubusercontent.com/118523/142730720-839f017e-38db-423e-b53f-39f5f0a0316f.png" width="700"/>
|
||||
|
||||
### Download
|
||||
- release from [github: ](https://github.com/skylot/jadx/releases/latest)
|
||||
- latest [unstable build](https://nightly.link/skylot/jadx/workflows/build/master)
|
||||
- release
|
||||
from [github: ](https://github.com/skylot/jadx/releases/latest)
|
||||
- latest [unstable build ](https://nightly.link/skylot/jadx/workflows/build-artifacts/master)
|
||||
|
||||
After download unpack zip file go to `bin` directory and run:
|
||||
- `jadx` - command line version
|
||||
@@ -45,20 +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
|
||||
```bash
|
||||
sudo pacman -S jadx
|
||||
```
|
||||
2. macOS
|
||||
```bash
|
||||
brew install jadx
|
||||
```
|
||||
- Arch Linux
|
||||
[](https://archlinux.org/packages/extra/any/jadx/)
|
||||
[](https://aur.archlinux.org/packages/jadx-git)
|
||||
```bash
|
||||
sudo pacman -S jadx
|
||||
```
|
||||
- macOS
|
||||
[](https://formulae.brew.sh/formula/jadx)
|
||||
```bash
|
||||
brew install jadx
|
||||
```
|
||||
- Flathub
|
||||
[](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 17 or higher must be installed:
|
||||
```
|
||||
git clone https://github.com/skylot/jadx.git
|
||||
cd jadx
|
||||
@@ -72,69 +86,135 @@ 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, .apkm, .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-rewrite-cfg - set '--deobf-cfg-file-mode' to 'overwrite' (deprecated)
|
||||
--deobf-use-sourcename - use source file name as class name alias
|
||||
--deobf-parse-kotlin-metadata - parse kotlin metadata to class and package names
|
||||
--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
|
||||
-j, --threads-count - processing threads count, default: 16
|
||||
--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 gradle project (set '--export-gradle-type' to 'auto')
|
||||
--export-gradle-type - Gradle project template for export:
|
||||
'auto' - detect automatically
|
||||
'android-app' - Android Application (apk)
|
||||
'android-library' - Android Library (aar)
|
||||
'simple-java' - simple Java
|
||||
-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
|
||||
--source-name-repeat-limit - allow using source name if it appears less than a limit number, default: 10
|
||||
--use-kotlin-methods-for-var-names - use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide, default: apply
|
||||
--use-headers-for-detect-resource-extensions - Use headers for detect resource extensions if resource obfuscated
|
||||
--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
|
||||
--type-update-limit - type update limit count (per one instruction), default: 10
|
||||
--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)
|
||||
--call-graph - save app call graph in format: 'dot' or 'json', default: none
|
||||
-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)
|
||||
--disable-plugins - comma separated list of plugin ids to disable
|
||||
--config <config-ref> - load configuration from file, <config-ref> can be:
|
||||
path to '.json' file
|
||||
short name - uses file with this name from config directory
|
||||
'none' - to disable config loading
|
||||
--save-config <config-ref> - save current options into configuration file and exit, <config-ref> can be:
|
||||
empty - for default config
|
||||
path to '.json' file
|
||||
short name - file will be saved in config directory
|
||||
--print-files - print files and directories used by jadx (config, cache, temp)
|
||||
--version - print jadx version
|
||||
-h, --help - print this help
|
||||
|
||||
Plugin options (-P<name>=<value>):
|
||||
1) dex-input (Load .dex and .apk files)
|
||||
-Pdex-input.verify-checksum - Verify dex file checksum before load, values: [yes, no], default: yes
|
||||
2) java-convert (Convert .jar and .class files to dex)
|
||||
-Pjava-convert.mode - Convert mode, values: [dx, d8, both], default: both
|
||||
-Pjava-convert.d8-desugar - Use desugar in d8, values: [yes, no], default: no
|
||||
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
|
||||
kotlin-smap: Use kotlin.SourceDebugExtension annotation for rename class alias
|
||||
- kotlin-smap.class-alias-source-dbg - rename class alias from SourceDebugExtension, values: [yes, no], default: no
|
||||
rename-mappings: various mappings support
|
||||
- rename-mappings.format - mapping format, values: [AUTO, TINY_FILE, TINY_2_FILE, ENIGMA_FILE, ENIGMA_DIR, PROGUARD_FILE, SRG_FILE, XSRG_FILE, JAM_FILE, CSRG_FILE, TSRG_FILE, TSRG_2_FILE, INTELLIJ_MIGRATION_MAP_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
|
||||
@@ -143,7 +223,25 @@ 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
|
||||
|
||||
Usage for `plugins` command
|
||||
```
|
||||
usage: plugins [options]
|
||||
options:
|
||||
-i, --install <locationId> - install plugin with locationId
|
||||
-j, --install-jar <path-to.jar> - install plugin from jar file
|
||||
-l, --list - list installed plugins
|
||||
-a, --available - list available plugins from jadx-plugins-list (aka marketplace)
|
||||
-u, --update - update installed plugins
|
||||
--uninstall <pluginId> - uninstall plugin with pluginId
|
||||
--disable <pluginId> - disable plugin with pluginId
|
||||
--enable <pluginId> - enable plugin with pluginId
|
||||
--list-all - list all plugins including bundled and dropins
|
||||
--list-versions <locationId> - fetch latest versions of plugin from locationId (will download all artefacts, limited to 10)
|
||||
-h, --help - print this help
|
||||
```
|
||||
|
||||
|
||||
### Troubleshooting
|
||||
Please check wiki page [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A)
|
||||
|
||||
+4
-3
@@ -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.
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>com.github.skylot.jadx</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>Apache-2.0</project_license>
|
||||
<name>JADX</name>
|
||||
<summary>Dex to Java decompiler</summary>
|
||||
<description>
|
||||
<p>Command line and GUI tools for producing Java source code from Android Dex and Apk files</p>
|
||||
<ul>
|
||||
<li>decompile Dalvik bytecode to java classes from APK, dex, aar, aab and zip files</li>
|
||||
<li>decode AndroidManifest.xml and other resources from resources.arsc</li>
|
||||
<li>deobfuscator included</li>
|
||||
<li>view decompiled code with highlighted syntax</li>
|
||||
<li>jump to declaration</li>
|
||||
<li>find usage</li>
|
||||
<li>full text search</li>
|
||||
<li>smali debugger</li>
|
||||
</ul>
|
||||
</description>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://user-images.githubusercontent.com/118523/142730720-839f017e-38db-423e-b53f-39f5f0a0316f.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<content_rating type="oars-1.1" />
|
||||
<launchable type="desktop-id">com.github.skylot.jadx.desktop</launchable>
|
||||
<url type="homepage">https://github.com/skylot/jadx</url>
|
||||
<url type="bugtracker">https://github.com/skylot/jadx/issues</url>
|
||||
<releases>
|
||||
<release version="1.3.4" date="2022-03-20" />
|
||||
</releases>
|
||||
</component>
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
plugins {
|
||||
id 'com.github.ben-manes.versions' version '0.42.0'
|
||||
id 'com.diffplug.spotless' version '6.5.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
|
||||
targetCompatibility = 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.5.1'
|
||||
testImplementation 'org.assertj:assertj-core:3.22.0'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
|
||||
|
||||
testCompileOnly 'org.jetbrains:annotations:23.0.0'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
maxParallelForks = Runtime.runtime.availableProcessors()
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
target fileTree(rootDir).matching {
|
||||
include 'jadx-cli/src/**/java/**/*.java'
|
||||
include 'jadx-core/src/**/java/**/*.java'
|
||||
include 'jadx-gui/src/**/java/**/*.java'
|
||||
include 'jadx-plugins/**/java/**/*.java'
|
||||
}
|
||||
|
||||
importOrderFile 'config/code-formatter/eclipse.importorder'
|
||||
eclipse().configFile 'config/code-formatter/eclipse.xml'
|
||||
if (JavaVersion.current() < JavaVersion.VERSION_16) {
|
||||
removeUnusedImports()
|
||||
} else {
|
||||
// google-format on Java 16+ issue: https://github.com/diffplug/spotless/issues/834
|
||||
println('Warning! Unused imports remove is disabled for Java 16+'
|
||||
+ ' (use workaround from https://github.com/diffplug/spotless/tree/main/plugin-gradle#google-java-format)')
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,223 @@
|
||||
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.54.0"
|
||||
id("se.patrikerdes.use-latest-versions") version "0.2.19"
|
||||
id("com.diffplug.spotless") version "8.7.0"
|
||||
}
|
||||
|
||||
val jadxEnv = loadEnv(file("$rootDir/.env"))
|
||||
|
||||
val jadxVersion by extra { jadxEnv["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 = jadxEnv[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
|
||||
}
|
||||
|
||||
// control ErrorProne checks level, can be: off, warn, error
|
||||
val jadxBuildChecksMode: String by extra { getBuildChecksMode() }
|
||||
|
||||
fun getBuildChecksMode(): String {
|
||||
val buildChecksMode = jadxEnv["JADX_BUILD_CHECKS_MODE"]?.lowercase() ?: "off"
|
||||
val expectedValues = listOf("off", "warn", "error")
|
||||
if (!expectedValues.contains(buildChecksMode)) {
|
||||
throw GradleException("Unknown check mode: '$buildChecksMode', should be one of $expectedValues")
|
||||
}
|
||||
if (buildChecksMode != "off") {
|
||||
val javaVersion = jadxBuildJavaVersion?.let { JavaVersion.toVersion(it) } ?: JavaVersion.current()
|
||||
if (!javaVersion.isCompatibleWith(JavaVersion.VERSION_21)) {
|
||||
throw GradleException("Error Prone requires Java 21")
|
||||
}
|
||||
}
|
||||
return buildChecksMode
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
fun loadEnv(file: File): Map<String, String> {
|
||||
val envMap = HashMap<String, String>()
|
||||
System
|
||||
.getenv()
|
||||
.filter { it.key.startsWith("JADX_") }
|
||||
.forEach { envMap[it.key] = it.value }
|
||||
if (file.exists()) {
|
||||
file
|
||||
.readLines()
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() && !it.startsWith("#") }
|
||||
.forEach {
|
||||
val (k, v) = it.split("=", limit = 2)
|
||||
envMap[k.trim()] = v.trim()
|
||||
}
|
||||
}
|
||||
println(
|
||||
"Loaded env vars (${envMap.size}):\n${
|
||||
envMap.toList().sortedBy { it.first }.joinToString(separator = "\n") { "${it.first}=${it.second}" }
|
||||
}\n",
|
||||
)
|
||||
return envMap
|
||||
}
|
||||
|
||||
val distWinConfiguration: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
}
|
||||
val distWinWithJreConfiguration: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
}
|
||||
dependencies {
|
||||
distWinConfiguration(project(":jadx-gui", "distWinConfiguration"))
|
||||
distWinWithJreConfiguration(project(":jadx-gui", "distWinWithJreConfiguration"))
|
||||
}
|
||||
|
||||
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)
|
||||
eachFile {
|
||||
if (path == "bin/jadx" || path == "bin/jadx-gui") {
|
||||
permissions {
|
||||
unix("rwxr-xr-x")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val distWin by tasks.registering(Zip::class) {
|
||||
group = "jadx"
|
||||
description = "Build Windows bundle"
|
||||
|
||||
from(distWinConfiguration)
|
||||
|
||||
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"
|
||||
|
||||
from(distWinWithJreConfiguration)
|
||||
|
||||
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)
|
||||
@@ -1,3 +0,0 @@
|
||||
plugins {
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.10")
|
||||
|
||||
implementation("org.openrewrite:plugin:6.19.1")
|
||||
implementation("net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:4.2.0")
|
||||
implementation("net.ltgt.nullaway:net.ltgt.nullaway.gradle.plugin:2.3.0")
|
||||
}
|
||||
|
||||
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,97 @@
|
||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
||||
import net.ltgt.gradle.errorprone.CheckSeverity
|
||||
import net.ltgt.gradle.errorprone.errorprone
|
||||
import net.ltgt.gradle.nullaway.nullaway
|
||||
|
||||
plugins {
|
||||
java
|
||||
checkstyle
|
||||
|
||||
id("jadx-rewrite")
|
||||
id("net.ltgt.errorprone")
|
||||
id("net.ltgt.nullaway")
|
||||
}
|
||||
|
||||
val jadxVersion: String by rootProject.extra
|
||||
val jadxBuildJavaVersion: Int? by rootProject.extra
|
||||
val jadxBuildChecksMode: String by rootProject.extra
|
||||
|
||||
group = "io.github.skylot"
|
||||
version = jadxVersion
|
||||
|
||||
dependencies {
|
||||
implementation("org.slf4j:slf4j-api:2.0.18")
|
||||
compileOnly("org.jetbrains:annotations:26.1.0")
|
||||
|
||||
testImplementation("ch.qos.logback:logback-classic:1.5.34")
|
||||
testImplementation("org.assertj:assertj-core:3.27.7")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.13.3")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
||||
testCompileOnly("org.jetbrains:annotations:26.1.0")
|
||||
|
||||
errorprone("com.google.errorprone:error_prone_core:2.50.0")
|
||||
errorprone("com.uber.nullaway:nullaway:0.13.7")
|
||||
}
|
||||
|
||||
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"
|
||||
// options.compilerArgs = listOf("-Xlint:deprecation")
|
||||
}
|
||||
jar {
|
||||
manifest {
|
||||
attributes("jadx-version" to jadxVersion)
|
||||
}
|
||||
}
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
maxParallelForks = Runtime.getRuntime().availableProcessors()
|
||||
testLogging {
|
||||
showExceptions = true
|
||||
exceptionFormat = TestExceptionFormat.FULL
|
||||
showCauses = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
val checkEnabled = jadxBuildChecksMode != "off"
|
||||
if (checkEnabled) {
|
||||
options.compilerArgs.add("-XDaddTypeAnnotationsToSymbol=true")
|
||||
}
|
||||
options.errorprone {
|
||||
isEnabled = checkEnabled
|
||||
allErrorsAsWarnings = jadxBuildChecksMode == "warn"
|
||||
excludedPaths = ".*/test/.*"
|
||||
nullaway {
|
||||
if (jadxBuildChecksMode == "error") {
|
||||
error()
|
||||
}
|
||||
annotatedPackages.add("jadx")
|
||||
}
|
||||
// TODO: fix and enable all checks
|
||||
disable("MixedMutabilityReturnType")
|
||||
disable("EqualsGetClass")
|
||||
disable("OperatorPrecedence")
|
||||
disable("UnusedVariable")
|
||||
disable("ImmutableEnumChecker")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
id("jadx-java")
|
||||
id("org.jetbrains.kotlin.jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(kotlin("reflect")) // don't work from plugin classloader
|
||||
}
|
||||
|
||||
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,35 @@
|
||||
plugins {
|
||||
id("org.openrewrite.rewrite")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
rewrite("org.openrewrite.recipe:rewrite-testing-frameworks:3.38.0")
|
||||
rewrite("org.openrewrite.recipe:rewrite-logging-frameworks:3.29.1")
|
||||
rewrite("org.openrewrite.recipe:rewrite-migrate-java:3.37.0")
|
||||
rewrite("org.openrewrite.recipe:rewrite-static-analysis:2.37.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")
|
||||
}
|
||||
}
|
||||
@@ -120,15 +120,22 @@
|
||||
|
||||
<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"/>
|
||||
<!-- don't use nullable annotations from RxJava -->
|
||||
<property name="illegalClasses" value="io.reactivex.rxjava3.annotations.NonNull"/>
|
||||
<property name="illegalClasses" value="io.reactivex.rxjava3.annotations.Nullable"/>
|
||||
</module>
|
||||
<module name="RegexpSinglelineJava">
|
||||
<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
@@ -1,77 +1,248 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<profiles version="16">
|
||||
<profile kind="CodeFormatterProfile" name="jadx eclipse" version="16">
|
||||
<profiles version="23">
|
||||
<profile kind="CodeFormatterProfile" name="jadx eclipse" version="23">
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="separate_lines_if_wrapped"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="49"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="100"/>
|
||||
@@ -80,267 +251,148 @@
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="separate_lines_if_wrapped"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="48"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="140"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||
|
||||
@@ -1,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; }
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[Desktop Entry]
|
||||
Name=JADX GUI
|
||||
Comment=Dex to Java decompiler
|
||||
Icon=jadx
|
||||
Exec=jadx-gui %f
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Development;Java;
|
||||
Keywords=Java;Decompiler;
|
||||
StartupWMClass=jadx-gui-JadxGUI
|
||||
@@ -1,2 +1,16 @@
|
||||
org.gradle.warning.mode=all
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
|
||||
### Disable configuration cache for now: causing issues with spotless and version plugins
|
||||
# org.gradle.configuration-cache=true
|
||||
# org.gradle.configuration-cache.problems=warn
|
||||
|
||||
# Flags for google-java-format (optimize imports by spotless) for Java >= 16.
|
||||
# Java < 9 will ignore unsupported flags (thanks to -XX:+IgnoreUnrecognizedVMOptions)
|
||||
org.gradle.jvmargs=-XX:+IgnoreUnrecognizedVMOptions \
|
||||
--add-exports='jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED' \
|
||||
--add-exports='jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED' \
|
||||
--add-exports='jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED' \
|
||||
--add-exports='jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED' \
|
||||
--add-exports='jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED'
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+4
-2
@@ -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=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -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/2d6327017519d23b96af35865dc997fcb544fb40/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,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -114,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -133,22 +132,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
|
||||
@@ -165,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -193,18 +198,27 @@ 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, 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" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# 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
+93
-89
@@ -1,89 +1,93 @@
|
||||
@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
|
||||
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
: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
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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.8"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":jadx-core"))
|
||||
implementation(project(":jadx-plugins-tools"))
|
||||
implementation(project(":jadx-commons:jadx-app-commons"))
|
||||
implementation(project(":jadx-commons:jadx-analysis"))
|
||||
|
||||
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-kotlin-source-debug-extension"))
|
||||
runtimeOnly(project(":jadx-plugins:jadx-xapk-input"))
|
||||
runtimeOnly(project(":jadx-plugins:jadx-aab-input"))
|
||||
runtimeOnly(project(":jadx-plugins:jadx-apkm-input"))
|
||||
runtimeOnly(project(":jadx-plugins:jadx-apks-input"))
|
||||
|
||||
implementation("org.jcommander:jcommander:2.0")
|
||||
implementation("ch.qos.logback:logback-classic:1.5.34")
|
||||
implementation("com.google.code.gson:gson:2.14.0")
|
||||
}
|
||||
|
||||
application {
|
||||
applicationName = "jadx"
|
||||
mainClass.set("jadx.cli.JadxCLI")
|
||||
applicationDefaultJvmArgs =
|
||||
listOf(
|
||||
"-XX:+IgnoreUnrecognizedVMOptions",
|
||||
"-Xms256M",
|
||||
"-XX:MaxRAMPercentage=70.0",
|
||||
"-XX:ParallelGCThreads=3",
|
||||
// disable zip checks (#1962)
|
||||
"-Djdk.util.zip.disableZip64ExtraFieldValidation=true",
|
||||
// Foreign API access for 'directories' library (Windows only)
|
||||
"--enable-native-access=ALL-UNNAMED",
|
||||
)
|
||||
applicationDistribution.from("$rootDir") {
|
||||
include("README.md")
|
||||
include("NOTICE")
|
||||
include("LICENSE")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
// shadow jar not needed
|
||||
configurations = listOf()
|
||||
}
|
||||
@@ -4,80 +4,191 @@ 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;
|
||||
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;
|
||||
|
||||
public class JCommanderWrapper<T> {
|
||||
public class JCommanderWrapper {
|
||||
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) {
|
||||
try {
|
||||
jc.parse(args);
|
||||
String[] fixedArgs = fixArgsForEmptySaveConfig(args);
|
||||
jc.parse(fixedArgs);
|
||||
applyFiles(argsObj);
|
||||
return true;
|
||||
} catch (ParameterException e) {
|
||||
System.err.println("Arguments parse error: " + e.getMessage());
|
||||
printUsage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void overrideProvided(T obj) {
|
||||
List<ParameterDescription> fieldsParams = jc.getParameters();
|
||||
List<ParameterDescription> parameters = new ArrayList<>(1 + fieldsParams.size());
|
||||
parameters.add(jc.getMainParameterValue());
|
||||
parameters.addAll(fieldsParams);
|
||||
for (ParameterDescription parameter : parameters) {
|
||||
public void overrideProvided(JadxCLIArgs obj) {
|
||||
applyFiles(obj);
|
||||
for (ParameterDescription parameter : jc.getParameters()) {
|
||||
if (parameter.isAssigned()) {
|
||||
// copy assigned field value to obj
|
||||
Parameterized parameterized = parameter.getParameterized();
|
||||
Object val = parameterized.get(parameter.getObject());
|
||||
parameterized.set(obj, val);
|
||||
overrideProperty(obj, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean processCommands() {
|
||||
String parsedCommand = jc.getParsedCommand();
|
||||
if (parsedCommand == null) {
|
||||
return false;
|
||||
}
|
||||
return JadxCLICommands.process(this, jc, parsedCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* The main parameter parsing doesn't work if accepting unknown options
|
||||
*/
|
||||
private void applyFiles(JadxCLIArgs argsObj) {
|
||||
argsObj.setFiles(jc.getUnknownOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Override assigned field value to obj
|
||||
*/
|
||||
private static void overrideProperty(JadxCLIArgs obj, ParameterDescription parameter) {
|
||||
Parameterized parameterized = parameter.getParameterized();
|
||||
Object providedValue = parameterized.get(parameter.getObject());
|
||||
Object newValue = mergeValues(parameterized.getType(), providedValue, () -> parameterized.get(obj));
|
||||
parameterized.set(obj, newValue);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private static Object mergeValues(Class<?> type, Object value, Supplier<Object> prevValueProvider) {
|
||||
if (type.isAssignableFrom(Map.class)) {
|
||||
// merge maps instead replacing whole map
|
||||
Map prevMap = (Map) prevValueProvider.get();
|
||||
return Utils.mergeMaps(prevMap, (Map) value); // value map will override keys in prevMap
|
||||
}
|
||||
// simple override
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround to allow empty value (i.e. zero arity) for '--save-config' option
|
||||
* Insert empty string arg if another option start right after this one, or it is a last one.
|
||||
*/
|
||||
private String[] fixArgsForEmptySaveConfig(String[] args) {
|
||||
int len = args.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
String arg = args[i];
|
||||
if (arg.equals("--save-config")) {
|
||||
int next = i + 1;
|
||||
if (next == len) {
|
||||
return insertEmptyArg(args, next, true);
|
||||
}
|
||||
if (next < len) {
|
||||
String nextArg = args[next];
|
||||
if (nextArg.startsWith("-")) {
|
||||
return insertEmptyArg(args, next, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private static String[] insertEmptyArg(String[] args, int i, boolean add) {
|
||||
List<String> strings = new ArrayList<>(Arrays.asList(args));
|
||||
if (add) {
|
||||
strings.add("");
|
||||
} else {
|
||||
strings.add(i, "");
|
||||
}
|
||||
return strings.toArray(new String[0]);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -86,8 +197,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]);
|
||||
@@ -99,26 +214,28 @@ 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
|
||||
&& !defaultValue.isEmpty()
|
||||
&& !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()));
|
||||
@@ -128,7 +245,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) {
|
||||
@@ -157,14 +274,20 @@ 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(decompiler.getArgs().getPluginLoader());
|
||||
pluginManager.initAll();
|
||||
try {
|
||||
for (PluginContext context : pluginManager.getAllPluginContexts()) {
|
||||
JadxPluginOptions options = context.getOptions();
|
||||
if (options != null) {
|
||||
appendPlugin(context.getPluginInfo(), context.getOptions(), sb, maxNamesLen);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
pluginManager.unloadAll();
|
||||
}
|
||||
}
|
||||
if (sb.length() == 0) {
|
||||
@@ -173,17 +296,16 @@ public class JCommanderWrapper<T> {
|
||||
return "\nPlugin options (-P<name>=<value>):" + sb;
|
||||
}
|
||||
|
||||
private boolean appendPlugin(JadxPluginOptions plugin, StringBuilder out, int maxNamesLen, int k) {
|
||||
List<OptionDescription> descs = plugin.getOptionsDescriptions();
|
||||
private boolean appendPlugin(JadxPluginInfo pluginInfo, JadxPluginOptions options, StringBuilder out, int maxNamesLen) {
|
||||
List<OptionDescription> descs = options.getOptionsDescriptions();
|
||||
if (descs.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
JadxPluginInfo pluginInfo = plugin.getPluginInfo();
|
||||
out.append("\n ").append(k).append(") ");
|
||||
out.append(pluginInfo.getPluginId()).append(" (").append(pluginInfo.getDescription()).append(") ");
|
||||
out.append("\n ");
|
||||
out.append(pluginInfo.getPluginId()).append(": ").append(pluginInfo.getDescription());
|
||||
for (OptionDescription desc : descs) {
|
||||
StringBuilder opt = new StringBuilder();
|
||||
opt.append(" -P").append(desc.name());
|
||||
opt.append(" - ").append(desc.name());
|
||||
addSpaces(opt, maxNamesLen - opt.length());
|
||||
opt.append("- ").append(desc.description());
|
||||
if (!desc.values().isEmpty()) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package jadx.cli;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.security.JadxSecurityFlag;
|
||||
import jadx.api.security.impl.JadxSecurity;
|
||||
import jadx.commons.app.JadxCommonEnv;
|
||||
import jadx.zip.security.DisabledZipSecurity;
|
||||
import jadx.zip.security.IJadxZipSecurity;
|
||||
import jadx.zip.security.JadxZipSecurity;
|
||||
|
||||
public class JadxAppCommon {
|
||||
|
||||
public static void applyEnvVars(JadxArgs jadxArgs) {
|
||||
Set<JadxSecurityFlag> flags = JadxSecurityFlag.all();
|
||||
IJadxZipSecurity zipSecurity;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
boolean disableZipSecurity = JadxCommonEnv.getBool("JADX_DISABLE_ZIP_SECURITY", false);
|
||||
if (disableZipSecurity) {
|
||||
flags.remove(JadxSecurityFlag.SECURE_ZIP_READER);
|
||||
zipSecurity = DisabledZipSecurity.INSTANCE;
|
||||
} else {
|
||||
JadxZipSecurity jadxZipSecurity = new JadxZipSecurity();
|
||||
int maxZipEntriesCount = JadxCommonEnv.getInt("JADX_ZIP_MAX_ENTRIES_COUNT", -2);
|
||||
if (maxZipEntriesCount != -2) {
|
||||
jadxZipSecurity.setMaxEntriesCount(maxZipEntriesCount);
|
||||
}
|
||||
int zipBombMinUncompressedSize = JadxCommonEnv.getInt("JADX_ZIP_BOMB_MIN_UNCOMPRESSED_SIZE", -2);
|
||||
if (zipBombMinUncompressedSize != -2) {
|
||||
jadxZipSecurity.setZipBombMinUncompressedSize(zipBombMinUncompressedSize);
|
||||
}
|
||||
int setZipBombDetectionFactor = JadxCommonEnv.getInt("JADX_ZIP_BOMB_DETECTION_FACTOR", -2);
|
||||
if (setZipBombDetectionFactor != -2) {
|
||||
jadxZipSecurity.setZipBombDetectionFactor(setZipBombDetectionFactor);
|
||||
}
|
||||
zipSecurity = jadxZipSecurity;
|
||||
}
|
||||
jadxArgs.setSecurity(new JadxSecurity(flags, zipSecurity));
|
||||
}
|
||||
}
|
||||
@@ -1,55 +1,83 @@
|
||||
package jadx.cli;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.analysis.callgraph.JadxCallGraph;
|
||||
import jadx.analysis.callgraph.api.ICallGraph;
|
||||
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.usage.impl.EmptyUsageInfoCache;
|
||||
import jadx.cli.LogHelper.LogLevelEnum;
|
||||
import jadx.cli.config.JadxConfigAdapter;
|
||||
import jadx.cli.plugins.JadxFilesGetter;
|
||||
import jadx.core.utils.exceptions.JadxArgsValidateException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
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);
|
||||
}
|
||||
return 0;
|
||||
return execute(args, null);
|
||||
}
|
||||
|
||||
private static int processAndSave(JadxCLIArgs cliArgs) {
|
||||
LogHelper.initLogLevel(cliArgs);
|
||||
LogHelper.setLogLevelsForLoadingStage();
|
||||
public static int execute(String[] args, @Nullable Consumer<JadxArgs> argsMod) {
|
||||
try {
|
||||
JadxCLIArgs cliArgs = JadxCLIArgs.processArgs(args,
|
||||
new JadxCLIArgs(),
|
||||
new JadxConfigAdapter<>(JadxCLIArgs.class, "cli"));
|
||||
if (cliArgs == null) {
|
||||
return 0;
|
||||
}
|
||||
JadxArgs jadxArgs = buildArgs(cliArgs);
|
||||
if (argsMod != null) {
|
||||
argsMod.accept(jadxArgs);
|
||||
}
|
||||
return runSave(jadxArgs, cliArgs);
|
||||
} catch (JadxArgsValidateException e) {
|
||||
LOG.error("Incorrect arguments: {}", e.getMessage());
|
||||
return 1;
|
||||
} catch (Throwable e) {
|
||||
LOG.error("Process error:", e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static JadxArgs buildArgs(JadxCLIArgs cliArgs) {
|
||||
JadxArgs jadxArgs = cliArgs.toJadxArgs();
|
||||
jadxArgs.setCodeCache(new NoOpCodeCache());
|
||||
jadxArgs.setCodeWriterProvider(SimpleCodeWriter::new);
|
||||
jadxArgs.setUsageInfoCache(new EmptyUsageInfoCache());
|
||||
jadxArgs.setPluginLoader(new JadxExternalPluginsLoader());
|
||||
jadxArgs.setFilesGetter(JadxFilesGetter.INSTANCE);
|
||||
initCodeWriterProvider(jadxArgs);
|
||||
JadxAppCommon.applyEnvVars(jadxArgs);
|
||||
return jadxArgs;
|
||||
}
|
||||
|
||||
private static int runSave(JadxArgs jadxArgs, JadxCLIArgs cliArgs) {
|
||||
try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
|
||||
jadx.load();
|
||||
if (checkForErrors(jadx)) {
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
LogHelper.setLogLevelsForDecompileStage();
|
||||
writeCallGraph(jadx, cliArgs);
|
||||
if (!SingleClassMode.process(jadx, cliArgs)) {
|
||||
save(jadx);
|
||||
}
|
||||
@@ -57,22 +85,40 @@ public class JadxCLI {
|
||||
if (errorsCount != 0) {
|
||||
jadx.printErrorsReport();
|
||||
LOG.error("finished with errors, count: {}", errorsCount);
|
||||
} else {
|
||||
LOG.info("done");
|
||||
return 3;
|
||||
}
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static boolean checkForErrors(JadxDecompiler jadx) {
|
||||
if (jadx.getRoot().getClasses().isEmpty()) {
|
||||
LOG.error("Load failed! No classes for decompile!");
|
||||
return true;
|
||||
if (jadx.getArgs().isSkipResources()) {
|
||||
LOG.error("Load failed! No classes for decompile!");
|
||||
return true;
|
||||
}
|
||||
if (!jadx.getArgs().isSkipSources()) {
|
||||
LOG.warn("No classes to decompile; decoding resources only");
|
||||
jadx.getArgs().setSkipSources(true);
|
||||
}
|
||||
}
|
||||
if (jadx.getErrorsCount() > 0) {
|
||||
LOG.error("Load with errors! Check log for details");
|
||||
int errorsCount = jadx.getErrorsCount();
|
||||
if (errorsCount > 0) {
|
||||
LOG.error("Loading finished with errors! Count: {}", errorsCount);
|
||||
// continue processing
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -81,6 +127,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);
|
||||
@@ -89,4 +136,29 @@ public class JadxCLI {
|
||||
System.out.print(" \r");
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeCallGraph(JadxDecompiler jadx, JadxCLIArgs cliArgs) {
|
||||
JadxCLIArgs.CallGraphSaveMode mode = cliArgs.callGraphSaveMode;
|
||||
if (mode == null || mode == JadxCLIArgs.CallGraphSaveMode.NONE) {
|
||||
return;
|
||||
}
|
||||
Path outPath = jadx.getArgs().getOutDir().toPath();
|
||||
ICallGraph callGraph = JadxCallGraph.builder(jadx)
|
||||
.resolvedOnly(true)
|
||||
.build();
|
||||
Path cgPath;
|
||||
switch (mode) {
|
||||
case JSON:
|
||||
cgPath = outPath.resolve("callgraph.json");
|
||||
callGraph.writeJson(cgPath);
|
||||
break;
|
||||
case DOT:
|
||||
cgPath = outPath.resolve("callgraph.dot");
|
||||
callGraph.writeDot(cgPath);
|
||||
break;
|
||||
default:
|
||||
throw new JadxRuntimeException("Unexpected call graph save mode: " + mode);
|
||||
}
|
||||
LOG.info("Call graph saved: {}", cgPath.toAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -45,28 +43,19 @@ public class LogHelper {
|
||||
return null;
|
||||
}
|
||||
if (args.quiet) {
|
||||
return LogLevelEnum.QUIET;
|
||||
}
|
||||
if (args.verbose) {
|
||||
return LogLevelEnum.DEBUG;
|
||||
args.logLevel = LogLevelEnum.QUIET;
|
||||
} else if (args.verbose) {
|
||||
args.logLevel = LogLevelEnum.DEBUG;
|
||||
}
|
||||
return args.logLevel;
|
||||
}
|
||||
|
||||
public static void setLogLevelsForLoadingStage() {
|
||||
if (logLevelValue == null) {
|
||||
return;
|
||||
}
|
||||
if (logLevelValue == LogLevelEnum.PROGRESS) {
|
||||
// show load errors
|
||||
LogHelper.applyLogLevel(LogLevelEnum.ERROR);
|
||||
fixForShowProgress();
|
||||
return;
|
||||
}
|
||||
public static void setLogLevel(LogLevelEnum newLogLevel) {
|
||||
logLevelValue = newLogLevel;
|
||||
applyLogLevel(logLevelValue);
|
||||
}
|
||||
|
||||
public static void setLogLevelsForDecompileStage() {
|
||||
public static void applyLogLevels() {
|
||||
if (logLevelValue == null) {
|
||||
return;
|
||||
}
|
||||
@@ -83,6 +72,9 @@ public class LogHelper {
|
||||
setLevelForClass(JadxCLI.class, Level.INFO);
|
||||
setLevelForClass(JadxDecompiler.class, Level.INFO);
|
||||
setLevelForClass(SingleClassMode.class, Level.INFO);
|
||||
|
||||
// show warnings and errors from input plugins
|
||||
setLevelForPackage("jadx.plugins.input", Level.WARN);
|
||||
}
|
||||
|
||||
private static void applyLogLevel(@NotNull LogLevelEnum logLevel) {
|
||||
@@ -119,18 +111,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,218 @@
|
||||
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.JadxPluginListEntry;
|
||||
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<JadxPluginListEntry> availableList = JadxPluginsList.getInstance().get();
|
||||
System.out.println("Available plugins: " + availableList.size());
|
||||
for (JadxPluginListEntry 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(formatDescription(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()
|
||||
+ ": " + formatDescription(plugin.getDescription()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatDescription(String desc) {
|
||||
if (desc.contains("\n")) {
|
||||
// remove new lines
|
||||
desc = desc.replaceAll("\\R+", " ");
|
||||
}
|
||||
int maxLen = 512;
|
||||
if (desc.length() > maxLen) {
|
||||
// truncate very long descriptions
|
||||
desc = desc.substring(0, maxLen) + " ...";
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
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,7 @@
|
||||
package jadx.cli.config;
|
||||
|
||||
/**
|
||||
* Marker interface for jadx config objects
|
||||
*/
|
||||
public interface IJadxConfig {
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package jadx.cli.config;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.FieldAttributes;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
|
||||
import jadx.commons.app.JadxCommonFiles;
|
||||
import jadx.core.utils.GsonUtils;
|
||||
import jadx.core.utils.exceptions.JadxArgsValidateException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class JadxConfigAdapter<T extends IJadxConfig> {
|
||||
private static final ExclusionStrategy GSON_EXCLUSION_STRATEGY = new ExclusionStrategy() {
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
return f.getAnnotation(JadxConfigExclude.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private final Class<T> configCls;
|
||||
private final String defaultConfigFileName;
|
||||
private final Gson gson;
|
||||
|
||||
private Path configPath;
|
||||
|
||||
public JadxConfigAdapter(Class<T> configCls, String defaultConfigName) {
|
||||
this(configCls, defaultConfigName, gsonBuilder -> {
|
||||
});
|
||||
}
|
||||
|
||||
public JadxConfigAdapter(Class<T> configCls, String defaultConfigName, Consumer<GsonBuilder> applyGsonOptions) {
|
||||
this.configCls = configCls;
|
||||
this.defaultConfigFileName = defaultConfigName + ".json";
|
||||
GsonBuilder gsonBuilder = GsonUtils.defaultGsonBuilder();
|
||||
gsonBuilder.setExclusionStrategies(GSON_EXCLUSION_STRATEGY);
|
||||
applyGsonOptions.accept(gsonBuilder);
|
||||
this.gson = gsonBuilder.create();
|
||||
}
|
||||
|
||||
public void useConfigRef(String configRef) {
|
||||
this.configPath = resolveConfigRef(configRef);
|
||||
}
|
||||
|
||||
public Path getConfigPath() {
|
||||
return configPath;
|
||||
}
|
||||
|
||||
public String getDefaultConfigFileName() {
|
||||
return defaultConfigFileName;
|
||||
}
|
||||
|
||||
public @Nullable T load() {
|
||||
if (!Files.isRegularFile(configPath)) {
|
||||
// file not found
|
||||
return null;
|
||||
}
|
||||
try (JsonReader reader = gson.newJsonReader(Files.newBufferedReader(configPath))) {
|
||||
return gson.fromJson(reader, configCls);
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to load config file: " + configPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(T configObject) {
|
||||
try {
|
||||
String jsonStr = gson.toJson(configObject, configCls);
|
||||
// don't use stream writer here because serialization errors will corrupt config
|
||||
Files.writeString(configPath, jsonStr);
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Failed to save config file: " + configPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public String objectToJsonString(T configObject) {
|
||||
return gson.toJson(configObject, configCls);
|
||||
}
|
||||
|
||||
public T jsonStringToObject(String jsonStr) {
|
||||
return gson.fromJson(jsonStr, configCls);
|
||||
}
|
||||
|
||||
private Path resolveConfigRef(String configRef) {
|
||||
if (configRef == null || configRef.isEmpty()) {
|
||||
// use default config file
|
||||
return JadxCommonFiles.getConfigDir().resolve(defaultConfigFileName);
|
||||
}
|
||||
if (configRef.contains("/") || configRef.contains("\\")) {
|
||||
if (!configRef.toLowerCase().endsWith(".json")) {
|
||||
throw new JadxArgsValidateException("Config file extension should be '.json'");
|
||||
}
|
||||
return Path.of(configRef);
|
||||
}
|
||||
// treat as a short name
|
||||
return JadxCommonFiles.getConfigDir().resolve(configRef + ".json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package jadx.cli.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface JadxConfigExclude {
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package jadx.cli.tools;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
@@ -11,8 +10,6 @@ import java.util.List;
|
||||
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 +17,12 @@ 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.xmlgen.ResTableBinaryParser;
|
||||
import jadx.zip.IZipEntry;
|
||||
import jadx.zip.ZipContent;
|
||||
import jadx.zip.ZipReader;
|
||||
|
||||
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);
|
||||
@@ -51,25 +54,25 @@ public class ConvertArscFile {
|
||||
LOG.info("Input entries count: {}", resMap.size());
|
||||
|
||||
RootNode root = new RootNode(new JadxArgs()); // not really needed
|
||||
ZipReader zipReader = new ZipReader();
|
||||
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())) {
|
||||
ZipEntry entry = zip.getEntry("resources.arsc");
|
||||
try (ZipContent zip = zipReader.open(resFile.toFile())) {
|
||||
IZipEntry entry = zip.searchEntry("resources.arsc");
|
||||
if (entry == null) {
|
||||
LOG.error("Failed to load \"resources.arsc\" from {}", resFile);
|
||||
continue;
|
||||
}
|
||||
try (InputStream inputStream = zip.getInputStream(entry)) {
|
||||
try (InputStream inputStream = entry.getInputStream()) {
|
||||
resTableParser.decode(inputStream);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Load resources.arsc from extracted file
|
||||
try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(resFile))) {
|
||||
try (InputStream inputStream = Files.newInputStream(resFile)) {
|
||||
resTableParser.decode(inputStream);
|
||||
}
|
||||
}
|
||||
@@ -84,6 +87,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();
|
||||
|
||||
@@ -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,10 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- jadx-gui -->
|
||||
<logger name="com.pinterest.ktlint" level="INFO"/>
|
||||
<logger name="guru.nidi.graphviz" level="WARN"/>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package jadx.cli;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.plugins.loader.JadxBasePluginLoader;
|
||||
import jadx.core.plugins.files.SingleDirFilesGetter;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
public class BaseCliIntegrationTest {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BaseCliIntegrationTest.class);
|
||||
|
||||
static final PathMatcher LOG_ALL_FILES = path -> {
|
||||
LOG.debug("File in result dir: {}", path);
|
||||
return true;
|
||||
};
|
||||
|
||||
@TempDir
|
||||
Path testDir;
|
||||
|
||||
Path outputDir;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
outputDir = testDir.resolve("output");
|
||||
}
|
||||
|
||||
int execJadxCli(String sampleName, String... options) {
|
||||
return execJadxCli(buildArgs(List.of(options), sampleName));
|
||||
}
|
||||
|
||||
int execJadxCli(String[] args) {
|
||||
return JadxCLI.execute(args, jadxArgs -> {
|
||||
// don't use global config and plugins
|
||||
jadxArgs.setFilesGetter(new SingleDirFilesGetter(testDir));
|
||||
jadxArgs.setPluginLoader(new JadxBasePluginLoader());
|
||||
});
|
||||
}
|
||||
|
||||
String[] buildArgs(List<String> options, String... inputSamples) {
|
||||
List<String> args = new ArrayList<>(options);
|
||||
args.add("-v");
|
||||
args.add("-d");
|
||||
args.add(outputDir.toAbsolutePath().toString());
|
||||
|
||||
for (String inputSample : inputSamples) {
|
||||
try {
|
||||
URL resource = getClass().getClassLoader().getResource(inputSample);
|
||||
assertThat(resource).isNotNull();
|
||||
String sampleFile = resource.toURI().getRawPath();
|
||||
args.add(sampleFile);
|
||||
} catch (URISyntaxException e) {
|
||||
fail("Failed to load sample: " + inputSample, e);
|
||||
}
|
||||
}
|
||||
return args.toArray(new String[0]);
|
||||
}
|
||||
|
||||
void decompile(String... inputSamples) throws IOException {
|
||||
int result = execJadxCli(buildArgs(List.of(), inputSamples));
|
||||
assertThat(result).isEqualTo(0);
|
||||
List<Path> resultJavaFiles = collectJavaFilesInDir(outputDir);
|
||||
assertThat(resultJavaFiles).isNotEmpty();
|
||||
|
||||
// do not copy input files as resources
|
||||
for (Path path : collectFilesInDir(outputDir, LOG_ALL_FILES)) {
|
||||
for (String inputSample : inputSamples) {
|
||||
assertThat(path.toAbsolutePath().toString()).doesNotContain(inputSample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void printFiles(List<Path> files) {
|
||||
LOG.info("Output files (count: {}):", files.size());
|
||||
for (Path file : files) {
|
||||
LOG.info(" {}", file);
|
||||
}
|
||||
LOG.info("");
|
||||
}
|
||||
|
||||
String pathToUniformString(Path path) {
|
||||
return path.toString().replace('\\', '/');
|
||||
}
|
||||
|
||||
Path printFileContent(Path file) {
|
||||
try {
|
||||
String content = Files.readString(outputDir.resolve(file));
|
||||
String spacer = Utils.strRepeat("=", 70);
|
||||
LOG.info("File content: {}\n{}\n{}\n{}", file, spacer, content, spacer);
|
||||
return file;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load file: " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
static List<Path> collectJavaFilesInDir(Path dir) throws IOException {
|
||||
PathMatcher javaMatcher = dir.getFileSystem().getPathMatcher("glob:**.java");
|
||||
return collectFilesInDir(dir, javaMatcher);
|
||||
}
|
||||
|
||||
static List<Path> collectAllFilesInDir(Path dir) throws IOException {
|
||||
try (Stream<Path> pathStream = Files.walk(dir)) {
|
||||
List<Path> files = pathStream
|
||||
.filter(Files::isRegularFile)
|
||||
.map(dir::relativize)
|
||||
.collect(Collectors.toList());
|
||||
printFiles(files);
|
||||
return files;
|
||||
}
|
||||
}
|
||||
|
||||
static List<Path> collectFilesInDir(Path dir, PathMatcher matcher) throws IOException {
|
||||
try (Stream<Path> pathStream = Files.walk(dir)) {
|
||||
List<Path> files = pathStream
|
||||
.filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS))
|
||||
.filter(matcher::matches)
|
||||
.collect(Collectors.toList());
|
||||
printFiles(files);
|
||||
return files;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
package jadx.cli;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static jadx.core.utils.Utils.newConstStringMap;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class JadxCLIArgsTest {
|
||||
|
||||
@@ -13,38 +16,72 @@ 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
|
||||
public void testPluginOptionsOverride() {
|
||||
// add key to empty base map
|
||||
checkPluginOptionsMerge(
|
||||
Collections.emptyMap(),
|
||||
"-Poption=otherValue",
|
||||
newConstStringMap("option", "otherValue"));
|
||||
|
||||
// override one key
|
||||
checkPluginOptionsMerge(
|
||||
newConstStringMap("option", "value"),
|
||||
"-Poption=otherValue",
|
||||
newConstStringMap("option", "otherValue"));
|
||||
|
||||
// merge different keys
|
||||
checkPluginOptionsMerge(
|
||||
Collections.singletonMap("option1", "value1"),
|
||||
"-Poption2=otherValue2",
|
||||
newConstStringMap("option1", "value1", "option2", "otherValue2"));
|
||||
|
||||
// merge and override
|
||||
checkPluginOptionsMerge(
|
||||
newConstStringMap("option1", "value1", "option2", "value2"),
|
||||
"-Poption2=otherValue2",
|
||||
newConstStringMap("option1", "value1", "option2", "otherValue2"));
|
||||
}
|
||||
|
||||
private void checkPluginOptionsMerge(Map<String, String> baseMap, String providedArgs, Map<String, String> expectedMap) {
|
||||
JadxCLIArgs args = new JadxCLIArgs();
|
||||
args.pluginOptions = baseMap;
|
||||
Map<String, String> resultMap = override(args, providedArgs).getPluginOptions();
|
||||
assertThat(resultMap).isEqualTo(expectedMap);
|
||||
}
|
||||
|
||||
private JadxCLIArgs parse(String... args) {
|
||||
@@ -52,15 +89,24 @@ 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, overrideProvided(jadxArgs, args));
|
||||
}
|
||||
|
||||
private static boolean overrideProvided(JadxCLIArgs jadxArgs, String[] args) {
|
||||
JCommanderWrapper jcw = new JCommanderWrapper(new JadxCLIArgs());
|
||||
if (!jcw.parse(args)) {
|
||||
return false;
|
||||
}
|
||||
jcw.overrideProvided(jadxArgs);
|
||||
return jadxArgs.process(jcw);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package jadx.cli;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestExport extends BaseCliIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void testBasicExport() throws Exception {
|
||||
int result = execJadxCli("samples/small.apk");
|
||||
assertThat(result).isEqualTo(0);
|
||||
assertThat(collectAllFilesInDir(outputDir))
|
||||
.map(this::pathToUniformString)
|
||||
.haveExactly(2, new Condition<>(f -> f.startsWith("sources/") && f.endsWith(".java"), "sources"))
|
||||
.haveExactly(10, new Condition<>(f -> f.startsWith("resources/"), "resources"))
|
||||
.haveExactly(1, new Condition<>(f -> f.equals("resources/AndroidManifest.xml"), "manifest"))
|
||||
.hasSize(12);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGradleExportApk() throws Exception {
|
||||
int result = execJadxCli("samples/small.apk", "--export-gradle");
|
||||
assertThat(result).isEqualTo(0);
|
||||
assertThat(collectAllFilesInDir(outputDir))
|
||||
.describedAs("check output files")
|
||||
.map(this::pathToUniformString)
|
||||
.haveExactly(2, new Condition<>(f -> f.endsWith(".java"), "java classes"))
|
||||
.haveExactly(0, new Condition<>(f -> f.endsWith("classes.dex"), "dex files"))
|
||||
.hasSize(15);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGradleExportAAR() throws Exception {
|
||||
int result = execJadxCli("samples/test-lib.aar", "--export-gradle");
|
||||
assertThat(result).isEqualTo(0);
|
||||
assertThat(collectAllFilesInDir(outputDir))
|
||||
.describedAs("check output files")
|
||||
.map(this::printFileContent)
|
||||
.map(this::pathToUniformString)
|
||||
.haveExactly(1, new Condition<>(f -> f.startsWith("lib/src/main/java/") && f.endsWith(".java"), "java"))
|
||||
.haveExactly(0, new Condition<>(f -> f.endsWith(".jar"), "jar files"))
|
||||
.hasSize(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGradleExportSimpleJava() throws Exception {
|
||||
int result = execJadxCli("samples/HelloWorld.class", "--export-gradle");
|
||||
assertThat(result).isEqualTo(0);
|
||||
assertThat(collectAllFilesInDir(outputDir))
|
||||
.describedAs("check output files")
|
||||
.map(this::printFileContent)
|
||||
.map(this::pathToUniformString)
|
||||
.haveExactly(1, new Condition<>(f -> f.endsWith(".java") && f.startsWith("app/src/main/java/"), "java"))
|
||||
.haveExactly(0, new Condition<>(f -> f.endsWith(".class"), "class files"))
|
||||
.haveExactly(1, new Condition<>(f -> f.equals("settings.gradle.kts"), "settings"))
|
||||
.haveExactly(1, new Condition<>(f -> f.equals("app/build.gradle.kts"), "build"))
|
||||
.hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGradleExportInvalidType() throws Exception {
|
||||
int result = execJadxCli("samples/HelloWorld.class", "--export-gradle-type", "android-app");
|
||||
assertThat(result).isEqualTo(0);
|
||||
// expect output in 'android-app' template, but most fields will be set to UNKNOWN.
|
||||
assertThat(collectAllFilesInDir(outputDir))
|
||||
.describedAs("check output files")
|
||||
.map(this::printFileContent)
|
||||
.map(this::pathToUniformString)
|
||||
.haveExactly(1, new Condition<>(f -> f.endsWith(".java") && f.startsWith("app/src/main/java/"), "java"))
|
||||
.haveExactly(1, new Condition<>(f -> f.equals("settings.gradle"), "settings"))
|
||||
.haveExactly(1, new Condition<>(f -> f.equals("build.gradle"), "build"))
|
||||
.haveExactly(1, new Condition<>(f -> f.equals("app/build.gradle"), "app build"))
|
||||
.hasSize(4);
|
||||
}
|
||||
}
|
||||
@@ -1,96 +1,84 @@
|
||||
package jadx.cli;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.util.ArrayList;
|
||||
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.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);
|
||||
public class TestInput extends BaseCliIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void testHelp() {
|
||||
int result = execJadxCli(new String[] { "--help" });
|
||||
assertThat(result).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApkInput() throws Exception {
|
||||
int result = execJadxCli(buildArgs(List.of(), "samples/small.apk"));
|
||||
assertThat(result).isEqualTo(0);
|
||||
assertThat(collectAllFilesInDir(outputDir))
|
||||
.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("AndroidManifest.xml"), "manifest"))
|
||||
.hasSize(12);
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
|
||||
private void decompile(String tmpDirName, String... inputSamples) throws URISyntaxException, IOException {
|
||||
List<String> args = new ArrayList<>();
|
||||
Path tempDir = FileUtils.createTempDir(tmpDirName);
|
||||
args.add("-v");
|
||||
args.add("-d");
|
||||
args.add(tempDir.toAbsolutePath().toString());
|
||||
|
||||
for (String inputSample : inputSamples) {
|
||||
URL resource = getClass().getClassLoader().getResource(inputSample);
|
||||
assertThat(resource).isNotNull();
|
||||
String sampleFile = resource.toURI().getRawPath();
|
||||
args.add(sampleFile);
|
||||
}
|
||||
|
||||
int result = JadxCLI.execute(args.toArray(new String[0]));
|
||||
@Test
|
||||
public void testFallbackMode() throws Exception {
|
||||
int result = execJadxCli(buildArgs(List.of("-f"), "samples/hello.dex"));
|
||||
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);
|
||||
}
|
||||
}
|
||||
List<Path> files = collectJavaFilesInDir(outputDir);
|
||||
assertThat(files).hasSize(1);
|
||||
}
|
||||
|
||||
private static List<Path> collectJavaFilesInDir(Path dir) throws IOException {
|
||||
PathMatcher javaMatcher = dir.getFileSystem().getPathMatcher("glob:**.java");
|
||||
return collectFilesInDir(dir, javaMatcher);
|
||||
@Test
|
||||
public void testSimpleMode() throws Exception {
|
||||
int result = execJadxCli(buildArgs(List.of("--decompilation-mode", "simple"), "samples/hello.dex"));
|
||||
assertThat(result).isEqualTo(0);
|
||||
List<Path> files = collectJavaFilesInDir(outputDir);
|
||||
assertThat(files).hasSize(1);
|
||||
}
|
||||
|
||||
private static List<Path> collectFilesInDir(Path dir, PathMatcher matcher) throws IOException {
|
||||
try (Stream<Path> pathStream = Files.walk(dir)) {
|
||||
return pathStream
|
||||
.filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS))
|
||||
.filter(matcher::matches)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@Test
|
||||
public void testResourceOnly() throws Exception {
|
||||
int result = execJadxCli(buildArgs(List.of(), "samples/resources-only.apk"));
|
||||
assertThat(result).isEqualTo(0);
|
||||
List<Path> files = collectFilesInDir(outputDir,
|
||||
path -> path.getFileName().toString().equalsIgnoreCase("AndroidManifest.xml"));
|
||||
assertThat(files).isNotEmpty();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void cleanup() {
|
||||
FileUtils.clearTempRootDir();
|
||||
@Test
|
||||
public void testNoRenameForDefPkg() throws Exception {
|
||||
int result = execJadxCli(buildArgs(List.of("--rename-flags", "none"), "samples/defpkg.smali"));
|
||||
assertThat(result).isEqualTo(0);
|
||||
List<Path> files = collectJavaFilesInDir(outputDir);
|
||||
assertThat(files).hasSize(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package jadx.plugins.tools.utils;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static jadx.plugins.tools.utils.PluginUtils.extractVersion;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class PluginUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testExtractVersion() {
|
||||
assertThat(extractVersion("plugin-name-v1.2.3.jar")).isEqualTo("1.2.3");
|
||||
assertThat(extractVersion("plugin-name-v1.2.jar")).isEqualTo("1.2");
|
||||
assertThat(extractVersion("1.2.3.jar")).isEqualTo("1.2.3");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
.class public LA;
|
||||
.super Ljava/lang/Object;
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,29 @@
|
||||
## jadx analysis
|
||||
|
||||
Various utilities for analyze and process code and related information.
|
||||
|
||||
|
||||
### Call graph
|
||||
|
||||
Full app code usage/call graph.
|
||||
Usage:
|
||||
```java
|
||||
JadxArgs args = new JadxArgs();
|
||||
args.addInputFile(new File("input.apk"));
|
||||
try (JadxDecompiler jadx = new JadxDecompiler(args)) {
|
||||
jadx.load();
|
||||
|
||||
ICallGraph callGraph = JadxCallGraph.builder(jadx)
|
||||
.includePackages("com.example") // filter nodes by package
|
||||
.resolvedOnly(true) // add nodes only from app (exclude framework/lib calls)
|
||||
.build();
|
||||
|
||||
for (ICallGraphEdge edge : callGraph.edges()) {
|
||||
if (edge.isResolved()) {
|
||||
System.out.printf("Edge from '%s' to '%s'%n", edge.from(), edge.to());
|
||||
}
|
||||
}
|
||||
callGraph.writeDot(Path.of("test.dot")); // export to '.dot'
|
||||
callGraph.writeJson(Path.of("test.json")); // export to JSON
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,12 @@
|
||||
plugins {
|
||||
id("jadx-library")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":jadx-core"))
|
||||
|
||||
implementation("com.google.code.gson:gson:2.14.0")
|
||||
|
||||
testRuntimeOnly(project(":jadx-plugins:jadx-dex-input"))
|
||||
testRuntimeOnly(project(":jadx-plugins:jadx-smali-input"))
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package jadx.analysis.callgraph;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.analysis.callgraph.api.ICallGraph;
|
||||
import jadx.analysis.callgraph.api.ICallGraphEdge;
|
||||
import jadx.api.JadxArgs;
|
||||
|
||||
class CallGraph implements ICallGraph {
|
||||
|
||||
private final JadxArgs args;
|
||||
private final List<ICallGraphEdge> edges;
|
||||
|
||||
public CallGraph(JadxArgs args, List<ICallGraphEdge> edges) {
|
||||
this.args = args;
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ICallGraphEdge> edges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDot(Path path) {
|
||||
new CallGraphExportDot(args, this).writeTo(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeJson(Path path) {
|
||||
new CallGraphExportJson(this).writeTo(path);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package jadx.analysis.callgraph;
|
||||
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
|
||||
class CallGraphAttrNode extends AttrNode {
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package jadx.analysis.callgraph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.analysis.callgraph.api.ICallGraph;
|
||||
import jadx.analysis.callgraph.api.ICallGraphBuilder;
|
||||
import jadx.analysis.callgraph.api.ICallGraphEdge;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
class CallGraphBuilder implements ICallGraphBuilder {
|
||||
private final JadxDecompiler decompiler;
|
||||
private boolean resolvedOnly = false;
|
||||
private @Nullable String pkgFilter;
|
||||
|
||||
public CallGraphBuilder(JadxDecompiler decompiler) {
|
||||
this.decompiler = decompiler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICallGraphBuilder resolvedOnly(boolean resolved) {
|
||||
this.resolvedOnly = resolved;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICallGraphBuilder includePackages(String pkgFilter) {
|
||||
this.pkgFilter = pkgFilter.endsWith(".") ? pkgFilter : pkgFilter + '.';
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICallGraph build() {
|
||||
return new CallGraph(decompiler.getArgs(), collectEdges());
|
||||
}
|
||||
|
||||
private List<ICallGraphEdge> collectEdges() {
|
||||
AtomicInteger nodeId = new AtomicInteger();
|
||||
Map<MethodInfo, CallGraphNode> nodes = new HashMap<>();
|
||||
List<ICallGraphEdge> edges = new ArrayList<>();
|
||||
|
||||
for (ClassNode cls : decompiler.getRoot().getClasses(true)) {
|
||||
if (ignorePkg(cls.getClassInfo())) {
|
||||
continue;
|
||||
}
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
CallGraphNode thisNode = getCallGraphNode(mth, nodes, nodeId);
|
||||
for (MethodNode use : mth.getUseIn()) {
|
||||
if (ignorePkg(use.getDeclaringClass().getClassInfo())) {
|
||||
continue;
|
||||
}
|
||||
CallGraphNode useInNode = getCallGraphNode(use, nodes, nodeId);
|
||||
edges.add(new CallGraphEdge(useInNode, thisNode));
|
||||
}
|
||||
if (!resolvedOnly) {
|
||||
for (MethodInfo used : mth.getUnresolvedUsed()) {
|
||||
if (ignorePkg(used.getDeclClass())) {
|
||||
continue;
|
||||
}
|
||||
CallGraphNode usedNode = getCallGraphNode(used, nodes, nodeId);
|
||||
edges.add(new CallGraphEdge(thisNode, usedNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
private boolean ignorePkg(ClassInfo cls) {
|
||||
if (pkgFilter == null) {
|
||||
return false;
|
||||
}
|
||||
return !cls.getFullName().startsWith(pkgFilter);
|
||||
}
|
||||
|
||||
private static CallGraphNode getCallGraphNode(MethodNode mth, Map<MethodInfo, CallGraphNode> nodes, AtomicInteger nodeId) {
|
||||
return nodes.computeIfAbsent(mth.getMethodInfo(), i -> new CallGraphNode(nodeId.incrementAndGet(), mth));
|
||||
}
|
||||
|
||||
private static CallGraphNode getCallGraphNode(MethodInfo mth, Map<MethodInfo, CallGraphNode> nodes, AtomicInteger nodeId) {
|
||||
return nodes.computeIfAbsent(mth, i -> new CallGraphNode(nodeId.incrementAndGet(), mth));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package jadx.analysis.callgraph;
|
||||
|
||||
import jadx.analysis.callgraph.api.ICallGraphEdge;
|
||||
import jadx.analysis.callgraph.api.ICallGraphNode;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
|
||||
class CallGraphEdge implements ICallGraphEdge {
|
||||
private final ICallGraphNode from;
|
||||
private final ICallGraphNode to;
|
||||
private final CallGraphAttrNode attrNode = new CallGraphAttrNode();
|
||||
|
||||
public CallGraphEdge(ICallGraphNode from, ICallGraphNode to) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICallGraphNode from() {
|
||||
return from;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICallGraphNode to() {
|
||||
return to;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResolved() {
|
||||
return to.isResolved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAttributeNode attributes() {
|
||||
return attrNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CallGraphEdge{from=" + from + ", to=" + to + '}';
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package jadx.analysis.callgraph;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jadx.analysis.callgraph.api.ICallGraph;
|
||||
import jadx.analysis.callgraph.api.ICallGraphEdge;
|
||||
import jadx.analysis.callgraph.api.ICallGraphNode;
|
||||
import jadx.api.ICodeWriter;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.impl.SimpleCodeWriter;
|
||||
import jadx.core.utils.DotGraphUtils;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
import static java.nio.file.StandardOpenOption.CREATE;
|
||||
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
|
||||
import static java.nio.file.StandardOpenOption.WRITE;
|
||||
|
||||
public class CallGraphExportDot {
|
||||
private final JadxArgs args;
|
||||
private final ICallGraph callGraph;
|
||||
|
||||
public CallGraphExportDot(JadxArgs args, ICallGraph callGraph) {
|
||||
this.args = args;
|
||||
this.callGraph = callGraph;
|
||||
}
|
||||
|
||||
public void writeTo(Path path) {
|
||||
try {
|
||||
FileUtils.makeDirsForFile(path);
|
||||
Files.writeString(path, writeToString(), StandardCharsets.UTF_8,
|
||||
WRITE, TRUNCATE_EXISTING, CREATE);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to save JSON file: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
public String writeToString() {
|
||||
// collect nodes
|
||||
Map<Integer, Node> nodeMap = new HashMap<>();
|
||||
for (ICallGraphEdge edge : callGraph.edges()) {
|
||||
addNode(edge.from(), nodeMap);
|
||||
addNode(edge.to(), nodeMap);
|
||||
}
|
||||
List<Node> nodes = new ArrayList<>(nodeMap.values());
|
||||
nodes.sort(Comparator.comparingInt(o -> o.id));
|
||||
|
||||
SimpleCodeWriter cw = new SimpleCodeWriter(args);
|
||||
cw.add("digraph CallGraph {");
|
||||
for (Node node : nodes) {
|
||||
cw.startLine();
|
||||
addNodeName(cw, node.id);
|
||||
cw.add("[shape=record,label=\"{");
|
||||
cw.add(DotGraphUtils.escape(node.method));
|
||||
cw.add("}\"];");
|
||||
}
|
||||
for (ICallGraphEdge edge : callGraph.edges()) {
|
||||
cw.startLine();
|
||||
addNodeName(cw, edge.from().getId());
|
||||
cw.add(" -> ");
|
||||
addNodeName(cw, edge.to().getId());
|
||||
cw.add(';');
|
||||
}
|
||||
cw.startLine('}');
|
||||
return cw.getCodeStr();
|
||||
}
|
||||
|
||||
private void addNodeName(ICodeWriter cw, int id) {
|
||||
cw.add('N').add(Integer.toString(id));
|
||||
}
|
||||
|
||||
private void addNode(ICallGraphNode cgNode, Map<Integer, Node> nodeMap) {
|
||||
nodeMap.computeIfAbsent(cgNode.getId(), id -> {
|
||||
Node node = new Node();
|
||||
node.id = id;
|
||||
node.method = cgNode.getMethodInfo().getRawFullId();
|
||||
return node;
|
||||
});
|
||||
}
|
||||
|
||||
static final class Node {
|
||||
int id;
|
||||
String method;
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package jadx.analysis.callgraph;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.Strictness;
|
||||
|
||||
import jadx.analysis.callgraph.api.ICallGraph;
|
||||
import jadx.analysis.callgraph.api.ICallGraphEdge;
|
||||
import jadx.analysis.callgraph.api.ICallGraphNode;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
import static java.nio.file.StandardOpenOption.CREATE;
|
||||
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
|
||||
import static java.nio.file.StandardOpenOption.WRITE;
|
||||
|
||||
public class CallGraphExportJson {
|
||||
private final ICallGraph callGraph;
|
||||
private final Gson gson;
|
||||
|
||||
public CallGraphExportJson(ICallGraph callGraph) {
|
||||
this.callGraph = callGraph;
|
||||
this.gson = new GsonBuilder()
|
||||
.disableJdkUnsafe()
|
||||
.disableInnerClassSerialization()
|
||||
.setStrictness(Strictness.STRICT)
|
||||
// .setPrettyPrinting() // TODO: add option for pretty print?
|
||||
.create();
|
||||
}
|
||||
|
||||
public void writeTo(Path path) {
|
||||
try {
|
||||
FileUtils.makeDirsForFile(path);
|
||||
Files.writeString(path, writeToString(), StandardCharsets.UTF_8,
|
||||
WRITE, TRUNCATE_EXISTING, CREATE);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to save JSON file: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
public String writeToString() {
|
||||
List<Edge> edges = new ArrayList<>();
|
||||
Map<Integer, Node> nodeMap = new HashMap<>();
|
||||
for (ICallGraphEdge edge : callGraph.edges()) {
|
||||
ICallGraphNode from = edge.from();
|
||||
ICallGraphNode to = edge.to();
|
||||
addNode(from, nodeMap);
|
||||
addNode(to, nodeMap);
|
||||
Edge jsonEdge = new Edge();
|
||||
jsonEdge.from = from.getId();
|
||||
jsonEdge.to = to.getId();
|
||||
jsonEdge.resolved = edge.isResolved();
|
||||
edges.add(jsonEdge);
|
||||
}
|
||||
List<Node> nodes = new ArrayList<>(nodeMap.values());
|
||||
nodes.sort(Comparator.comparingInt(o -> o.id));
|
||||
|
||||
RootNode rootNode = new RootNode();
|
||||
rootNode.nodes = nodes;
|
||||
rootNode.edges = edges;
|
||||
return gson.toJson(rootNode);
|
||||
}
|
||||
|
||||
private void addNode(ICallGraphNode cgNode, Map<Integer, Node> nodeMap) {
|
||||
nodeMap.computeIfAbsent(cgNode.getId(), id -> {
|
||||
Node node = new Node();
|
||||
node.id = id;
|
||||
node.method = cgNode.getMethodInfo().getRawFullId();
|
||||
return node;
|
||||
});
|
||||
}
|
||||
|
||||
static final class RootNode {
|
||||
List<Node> nodes;
|
||||
List<Edge> edges;
|
||||
}
|
||||
|
||||
static final class Node {
|
||||
int id;
|
||||
String method;
|
||||
}
|
||||
|
||||
static final class Edge {
|
||||
int from;
|
||||
int to;
|
||||
boolean resolved;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package jadx.analysis.callgraph;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.analysis.callgraph.api.ICallGraphNode;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
class CallGraphNode implements ICallGraphNode {
|
||||
private final int id;
|
||||
private final MethodInfo mthInfo;
|
||||
private final @Nullable MethodNode mthNode;
|
||||
private final CallGraphAttrNode attrNode;
|
||||
|
||||
public CallGraphNode(int id, MethodInfo mthInfo) {
|
||||
this(id, mthInfo, null);
|
||||
}
|
||||
|
||||
public CallGraphNode(int id, MethodNode mthNode) {
|
||||
this(id, mthNode.getMethodInfo(), mthNode);
|
||||
}
|
||||
|
||||
public CallGraphNode(int id, MethodInfo mthInfo, @Nullable MethodNode mthNode) {
|
||||
this.id = id;
|
||||
this.mthInfo = mthInfo;
|
||||
this.mthNode = mthNode;
|
||||
this.attrNode = new CallGraphAttrNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodInfo getMethodInfo() {
|
||||
return mthInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable MethodNode getMethodNode() {
|
||||
return mthNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResolved() {
|
||||
return mthNode != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAttributeNode attributes() {
|
||||
return attrNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mthInfo.getFullId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package jadx.analysis.callgraph;
|
||||
|
||||
import jadx.analysis.callgraph.api.ICallGraphBuilder;
|
||||
import jadx.api.JadxDecompiler;
|
||||
|
||||
public class JadxCallGraph {
|
||||
|
||||
public static ICallGraphBuilder builder(JadxDecompiler decompiler) {
|
||||
return new CallGraphBuilder(decompiler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package jadx.analysis.callgraph.api;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public interface ICallGraph {
|
||||
|
||||
List<ICallGraphEdge> edges();
|
||||
|
||||
void writeDot(Path path);
|
||||
|
||||
void writeJson(Path path);
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package jadx.analysis.callgraph.api;
|
||||
|
||||
public interface ICallGraphBuilder {
|
||||
|
||||
ICallGraphBuilder includePackages(String pkgFilter);
|
||||
|
||||
ICallGraphBuilder resolvedOnly(boolean resolved);
|
||||
|
||||
ICallGraph build();
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package jadx.analysis.callgraph.api;
|
||||
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
|
||||
public interface ICallGraphEdge {
|
||||
|
||||
ICallGraphNode from();
|
||||
|
||||
ICallGraphNode to();
|
||||
|
||||
boolean isResolved();
|
||||
|
||||
IAttributeNode attributes();
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package jadx.analysis.callgraph.api;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
public interface ICallGraphNode {
|
||||
|
||||
int getId();
|
||||
|
||||
MethodInfo getMethodInfo();
|
||||
|
||||
@Nullable
|
||||
MethodNode getMethodNode();
|
||||
|
||||
boolean isResolved();
|
||||
|
||||
IAttributeNode attributes();
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package jadx.analysis.callgraph.test;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import jadx.analysis.callgraph.CallGraphExportDot;
|
||||
import jadx.analysis.callgraph.CallGraphExportJson;
|
||||
import jadx.analysis.callgraph.JadxCallGraph;
|
||||
import jadx.analysis.callgraph.api.ICallGraph;
|
||||
import jadx.analysis.callgraph.api.ICallGraphEdge;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.JadxDecompiler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class JadxCallGraphTest {
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
void usageExample() {
|
||||
JadxArgs args = new JadxArgs();
|
||||
args.addInputFile(new File("input.apk"));
|
||||
try (JadxDecompiler jadx = new JadxDecompiler(args)) {
|
||||
jadx.load();
|
||||
|
||||
ICallGraph callGraph = JadxCallGraph.builder(jadx)
|
||||
.includePackages("com.example")
|
||||
.resolvedOnly(false)
|
||||
.build();
|
||||
|
||||
for (ICallGraphEdge edge : callGraph.edges()) {
|
||||
if (edge.isResolved()) {
|
||||
System.out.printf("Edge from '%s' to '%s'%n", edge.from(), edge.to());
|
||||
}
|
||||
}
|
||||
callGraph.writeDot(Path.of("test.dot"));
|
||||
callGraph.writeJson(Path.of("test.json"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleTest() {
|
||||
JadxArgs args = new JadxArgs();
|
||||
args.addInputFile(getSampleFile("simple.smali"));
|
||||
try (JadxDecompiler jadx = new JadxDecompiler(args)) {
|
||||
jadx.load();
|
||||
|
||||
ICallGraph callGraph = JadxCallGraph.builder(jadx)
|
||||
.includePackages("test.pkg")
|
||||
.resolvedOnly(false)
|
||||
.build();
|
||||
|
||||
assertThat(callGraph.edges()).hasSize(1);
|
||||
|
||||
for (ICallGraphEdge edge : callGraph.edges()) {
|
||||
System.out.println("Edge from " + edge.from() + " to " + edge.to());
|
||||
}
|
||||
|
||||
String dotStr = new CallGraphExportDot(jadx.getArgs(), callGraph).writeToString();
|
||||
System.out.println("dot: " + dotStr);
|
||||
|
||||
String jsonStr = new CallGraphExportJson(callGraph).writeToString();
|
||||
System.out.println("json: " + jsonStr);
|
||||
|
||||
callGraph.writeDot(tempDir.resolve("test.dot"));
|
||||
callGraph.writeJson(tempDir.resolve("test.json"));
|
||||
}
|
||||
}
|
||||
|
||||
private File getSampleFile(String sampleName) {
|
||||
try {
|
||||
URL resource = getClass().getResource("/samples/" + sampleName);
|
||||
assertThat(resource).describedAs("Sample not found: %s", sampleName).isNotNull();
|
||||
return new File(resource.toURI().toURL().getFile());
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
throw new RuntimeException("Failed to load sample file: " + sampleName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
.class Ltest/pkg/HelloWorld;
|
||||
.super Ljava/lang/Object;
|
||||
.source "HelloWorld.java"
|
||||
|
||||
.method public static main([Ljava/lang/String;)V
|
||||
.registers 2
|
||||
|
||||
const-string v0, "Hello, World"
|
||||
invoke-static {p0, v0}, Ltest/pkg/HelloWorld;->hello(Ljava/lang/String;)V
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static hello(Ljava/lang/String;)V
|
||||
.registers 2
|
||||
|
||||
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
|
||||
invoke-virtual {v0, p0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||
return-void
|
||||
.end method
|
||||
@@ -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("io.get-coursier.util:directories-jni:0.1.4")
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package jadx.commons.app;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JadxCommonEnv {
|
||||
|
||||
public static @Nullable String get(String varName, @Nullable 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(@Nullable String value) {
|
||||
return value == null || value.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package jadx.commons.app;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import dev.dirs.ProjectDirectories;
|
||||
import dev.dirs.impl.Windows;
|
||||
import dev.dirs.impl.WindowsPowerShell;
|
||||
import dev.dirs.jni.WindowsJni;
|
||||
|
||||
public class JadxCommonFiles {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxCommonFiles.class);
|
||||
|
||||
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();
|
||||
CONFIG_DIR = loader.getConfigDir();
|
||||
CACHE_DIR = loader.getCacheDir();
|
||||
}
|
||||
|
||||
private static final class DirsLoader {
|
||||
private final Path configDir;
|
||||
private final Path cacheDir;
|
||||
|
||||
DirsLoader() {
|
||||
try {
|
||||
AtomicReference<@Nullable ProjectDirectories> pdRef = new AtomicReference<>();
|
||||
configDir = loadEnvDir("JADX_CONFIG_DIR", () -> loadDirs(pdRef).configDir);
|
||||
cacheDir = loadEnvDir("JADX_CACHE_DIR", () -> loadDirs(pdRef).cacheDir);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to init common directories", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path loadEnvDir(String envVar, Supplier<String> dirFunc) throws IOException {
|
||||
String envDir = JadxCommonEnv.get(envVar, null);
|
||||
String dirStr;
|
||||
if (envDir != null) {
|
||||
dirStr = envDir;
|
||||
} else {
|
||||
dirStr = dirFunc.get();
|
||||
}
|
||||
Path path = Path.of(dirStr).toAbsolutePath();
|
||||
Files.createDirectories(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
private static ProjectDirectories loadDirs(AtomicReference<@Nullable ProjectDirectories> pdRef) {
|
||||
ProjectDirectories currentDirs = pdRef.get();
|
||||
if (currentDirs != null) {
|
||||
return currentDirs;
|
||||
}
|
||||
LOG.debug("Loading system dirs ...");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
ProjectDirectories loadedDirs = ProjectDirectories.from("io.github", "skylot", "jadx", DirsLoader::getWinDirs);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Loaded system dirs ({}ms): config: {}, cache: {}",
|
||||
System.currentTimeMillis() - start, loadedDirs.configDir, loadedDirs.cacheDir);
|
||||
}
|
||||
pdRef.set(loadedDirs);
|
||||
return loadedDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JNI, Foreign or PowerShell implementation
|
||||
*/
|
||||
private static Windows getWinDirs() {
|
||||
Windows impl = Windows.getDefaultSupplier().get();
|
||||
if (impl instanceof WindowsPowerShell) {
|
||||
if (JadxSystemInfo.IS_AMD64) {
|
||||
// JNI library compiled only for x86-64
|
||||
impl = new WindowsJni();
|
||||
}
|
||||
}
|
||||
LOG.debug("Using win dirs implementation: {}", impl.getClass().getSimpleName());
|
||||
return impl;
|
||||
}
|
||||
|
||||
Path getCacheDir() {
|
||||
return cacheDir;
|
||||
}
|
||||
|
||||
Path getConfigDir() {
|
||||
return configDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package jadx.commons.app;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class JadxSystemInfo {
|
||||
public static final String JAVA_VM = System.getProperty("java.vm.name", "?");
|
||||
public static final String JAVA_VER = System.getProperty("java.version", "?");
|
||||
|
||||
public static final String OS_NAME = System.getProperty("os.name", "?");
|
||||
public static final String OS_ARCH = System.getProperty("os.arch", "?");
|
||||
public static final String OS_VERSION = System.getProperty("os.version", "?");
|
||||
|
||||
private static final String OS_NAME_LOWER = OS_NAME.toLowerCase(Locale.ENGLISH);
|
||||
public static final boolean IS_WINDOWS = OS_NAME_LOWER.startsWith("windows");
|
||||
public static final boolean IS_MAC = OS_NAME_LOWER.startsWith("mac");
|
||||
public static final boolean IS_LINUX = !IS_WINDOWS && !IS_MAC;
|
||||
public static final boolean IS_UNIX = !IS_WINDOWS;
|
||||
|
||||
private static final String OS_ARCH_LOWER = OS_ARCH.toLowerCase(Locale.ENGLISH);
|
||||
public static final boolean IS_AMD64 = OS_ARCH_LOWER.equals("amd64");
|
||||
public static final boolean IS_ARM64 = OS_ARCH_LOWER.equals("aarch64");
|
||||
|
||||
private JadxSystemInfo() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
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) {
|
||||
Path customTmpRootDir = Paths.get(jadxTmpDir);
|
||||
Files.createDirectories(customTmpRootDir);
|
||||
dir = Files.createTempDirectory(customTmpRootDir, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
## jadx zip
|
||||
|
||||
Custom zip reader implementation to fight tampering and provide additional security checks
|
||||
@@ -0,0 +1,3 @@
|
||||
plugins {
|
||||
id("jadx-library")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package jadx.zip;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface IZipEntry {
|
||||
|
||||
/**
|
||||
* Zip entry name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Uncompressed bytes
|
||||
*/
|
||||
byte[] getBytes();
|
||||
|
||||
/**
|
||||
* Stream of uncompressed bytes.
|
||||
*/
|
||||
InputStream getInputStream();
|
||||
|
||||
long getCompressedSize();
|
||||
|
||||
long getUncompressedSize();
|
||||
|
||||
boolean isDirectory();
|
||||
|
||||
File getZipFile();
|
||||
|
||||
/**
|
||||
* Return true if {@link #getBytes()} method is more optimal to use other than
|
||||
* {@link #getInputStream()}
|
||||
*/
|
||||
boolean preferBytes();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package jadx.zip;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IZipParser extends Closeable {
|
||||
|
||||
ZipContent open() throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package jadx.zip;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ZipContent implements Closeable {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZipContent.class);
|
||||
|
||||
private final IZipParser zipParser;
|
||||
private final List<IZipEntry> entries;
|
||||
private final Map<String, IZipEntry> entriesMap;
|
||||
|
||||
public ZipContent(IZipParser zipParser, List<IZipEntry> entries) {
|
||||
this.zipParser = zipParser;
|
||||
this.entries = entries;
|
||||
this.entriesMap = buildNameMap(zipParser, entries);
|
||||
}
|
||||
|
||||
private static Map<String, IZipEntry> buildNameMap(IZipParser zipParser, List<IZipEntry> entries) {
|
||||
Map<String, IZipEntry> map = new HashMap<>(entries.size());
|
||||
for (IZipEntry entry : entries) {
|
||||
String name = entry.getName();
|
||||
IZipEntry prevEntry = map.put(name, entry);
|
||||
if (prevEntry != null) {
|
||||
LOG.warn("Found duplicate entry: {} in {}", name, zipParser);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public List<IZipEntry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public @Nullable IZipEntry searchEntry(String fileName) {
|
||||
return entriesMap.get(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
zipParser.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package jadx.zip;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.zip.fallback.FallbackException;
|
||||
import jadx.zip.fallback.FallbackZipParser;
|
||||
import jadx.zip.parser.JadxZipParser;
|
||||
import jadx.zip.security.IJadxZipSecurity;
|
||||
import jadx.zip.security.JadxZipSecurity;
|
||||
|
||||
/**
|
||||
* Jadx wrapper to provide custom zip parser ({@link JadxZipParser})
|
||||
* with fallback to default Java implementation.
|
||||
*/
|
||||
public class ZipReader {
|
||||
private final ZipReaderOptions options;
|
||||
|
||||
public ZipReader() {
|
||||
this(ZipReaderOptions.getDefault());
|
||||
}
|
||||
|
||||
public ZipReader(Set<ZipReaderFlags> flags) {
|
||||
this(new ZipReaderOptions(new JadxZipSecurity(), flags));
|
||||
}
|
||||
|
||||
public ZipReader(IJadxZipSecurity security) {
|
||||
this(new ZipReaderOptions(security, ZipReaderFlags.none()));
|
||||
}
|
||||
|
||||
public ZipReader(ZipReaderOptions options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public ZipContent open(File zipFile) throws IOException {
|
||||
if (!zipFile.exists()) {
|
||||
throw new FileNotFoundException(zipFile.getAbsolutePath());
|
||||
}
|
||||
try {
|
||||
JadxZipParser jadxParser = new JadxZipParser(zipFile, options);
|
||||
IZipParser detectedParser = detectParser(zipFile, jadxParser);
|
||||
return detectedParser.open();
|
||||
} catch (FallbackException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
if (options.getFlags().contains(ZipReaderFlags.DONT_USE_FALLBACK)) {
|
||||
throw new IOException("Failed to open zip: " + zipFile, e);
|
||||
}
|
||||
// switch to fallback parser
|
||||
return buildFallbackParser(zipFile).open();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit valid entries in a zip file.
|
||||
* Return not null value from visitor to stop iteration.
|
||||
*/
|
||||
public <R> @Nullable R visitEntries(File file, Function<IZipEntry, R> visitor) {
|
||||
try (ZipContent content = open(file)) {
|
||||
for (IZipEntry entry : content.getEntries()) {
|
||||
R result = visitor.apply(entry);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to process zip file: " + file.getAbsolutePath(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void readEntries(File file, BiConsumer<IZipEntry, InputStream> visitor) {
|
||||
visitEntries(file, entry -> {
|
||||
if (!entry.isDirectory()) {
|
||||
try (InputStream in = entry.getInputStream()) {
|
||||
visitor.accept(entry, in);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to process zip entry: " + entry, e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public ZipReaderOptions getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
private IZipParser detectParser(File zipFile, JadxZipParser jadxParser) throws IOException {
|
||||
if (zipFile.getName().endsWith(".apk")
|
||||
|| options.getFlags().contains(ZipReaderFlags.DONT_USE_FALLBACK)) {
|
||||
return jadxParser;
|
||||
}
|
||||
if (!jadxParser.canOpen()) {
|
||||
return buildFallbackParser(zipFile);
|
||||
}
|
||||
// default
|
||||
if (options.getFlags().contains(ZipReaderFlags.FALLBACK_AS_DEFAULT)) {
|
||||
return buildFallbackParser(zipFile);
|
||||
}
|
||||
return jadxParser;
|
||||
}
|
||||
|
||||
private FallbackZipParser buildFallbackParser(File zipFile) throws IOException {
|
||||
return new FallbackZipParser(zipFile, options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package jadx.zip;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
public enum ZipReaderFlags {
|
||||
/**
|
||||
* Search all local file headers by signature without reading
|
||||
* 'central directory' and 'end of central directory' entries
|
||||
*/
|
||||
IGNORE_CENTRAL_DIR_ENTRIES,
|
||||
|
||||
/**
|
||||
* Enable additional checks to verify zip data and report possible tampering
|
||||
*/
|
||||
REPORT_TAMPERING,
|
||||
|
||||
/**
|
||||
* Use fallback (java built-in implementation) parser as default.
|
||||
* Custom implementation will be used for '*.apk' files only.
|
||||
*/
|
||||
FALLBACK_AS_DEFAULT,
|
||||
|
||||
/**
|
||||
* Use only jadx custom parser and do not switch to fallback on errors.
|
||||
*/
|
||||
DONT_USE_FALLBACK;
|
||||
|
||||
public static Set<ZipReaderFlags> none() {
|
||||
return EnumSet.noneOf(ZipReaderFlags.class);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user