Compare commits
1990 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b8793155c | |||
| 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 | |||
| 21e94d8d5c | |||
| 7b1c7b967a | |||
| e4b19ab560 | |||
| 49137c9751 | |||
| 0606c90f22 | |||
| 65ade379a6 | |||
| a06df187c9 | |||
| e784c7f7df | |||
| a717392379 | |||
| a71b3a71d8 | |||
| 3366bf3dec | |||
| a505534197 | |||
| 357706b070 | |||
| e02434d135 | |||
| 4586015fc0 | |||
| 1832f2aee3 | |||
| 1ec127c3cb | |||
| 7a3b7c55c9 | |||
| b66293a2f7 | |||
| abcaafa89a | |||
| cf25cc4faa | |||
| b57001d4a7 | |||
| 83decc2473 | |||
| 92faa569be | |||
| c5b731169d | |||
| f0a8ef81d3 | |||
| 994973ac01 | |||
| c9622c0771 | |||
| 8551c6c903 | |||
| 9a9ac4308e | |||
| e784cbdd09 | |||
| 2744c4bfb6 | |||
| e4f4c1b84a | |||
| e5fa818b5c | |||
| b22b554a69 | |||
| e9b8060889 | |||
| 1c2b2c072c | |||
| 3d451912ee | |||
| fe91d774fa | |||
| d8306cb1c0 | |||
| 909cf0a576 | |||
| 8fe1ee11e4 | |||
| d2bef108f5 | |||
| ba8ba504b1 | |||
| 481b5abf85 | |||
| c4e1d9445a | |||
| cb03532b76 | |||
| c93e9eea14 | |||
| 9a67b19973 | |||
| 95c75bed1e | |||
| b008568a5c | |||
| 94fb91cec6 | |||
| c54dd77f35 | |||
| 17fbc99f29 | |||
| 21dd17290b | |||
| dc73fc92be | |||
| 592215db66 | |||
| fb318e3bd9 | |||
| 5f3c8816a3 | |||
| 6016b902c7 | |||
| 5852da1e3d | |||
| 502fd069be | |||
| fad9e7b827 | |||
| 35116d0b1a | |||
| 3b781e41ad | |||
| a3e9744364 | |||
| 7030daeccd | |||
| e7151ad7b2 | |||
| ed2a3c8458 | |||
| 779f75cd52 | |||
| 54683e3198 | |||
| 09335395f5 | |||
| 57e3dd8f15 | |||
| a9bbadd602 | |||
| 2c570681f7 | |||
| 25166970cc | |||
| d3a0a56b8b | |||
| 3c2c198a0e | |||
| 4d4d67f0b4 | |||
| 97e8a34906 | |||
| 82f3b57e83 | |||
| af2f14f807 | |||
| fe248d7098 | |||
| 1a2e702b25 | |||
| 1da20b8e7d | |||
| 01f74ff706 | |||
| 89e95eb9ee | |||
| a61ebaaa00 | |||
| 7a5a2fcd84 | |||
| 8d5554f1b5 | |||
| 873aabb471 | |||
| 4bed9dc358 | |||
| e229874195 | |||
| 473b6e31e9 | |||
| b5ce460618 | |||
| 3c05b05196 | |||
| bdb2efdb6b | |||
| a27ba3ff4b | |||
| 4684207b54 | |||
| dd1be3039b | |||
| 8b30b770cd | |||
| 47caa91e85 | |||
| d71f3e09df | |||
| 06c7415827 | |||
| bd3e62617e | |||
| 00b48473a0 | |||
| 84facb13d0 | |||
| 96f90e18e8 | |||
| 8ff18e63ee | |||
| 381405ea99 | |||
| ae5c00397a | |||
| bd4509f1a7 | |||
| b8c84886a8 | |||
| 45021389bc | |||
| f674a29a64 | |||
| 0c9e3227d0 | |||
| be7e1479a1 | |||
| 19827fca20 | |||
| 5eb7cc40ed | |||
| d22db30166 | |||
| 6db61e7a59 | |||
| 86582de521 | |||
| a7c63c2eb3 | |||
| 081a0e21ee | |||
| 9ac9c05265 | |||
| b7daf79b26 | |||
| b67a3561a4 | |||
| 52ac6dbbaf | |||
| 72381ad8f3 | |||
| 6a065c46f4 | |||
| 092d0d7e67 | |||
| 5ca7285558 | |||
| 7576f9cd5e | |||
| 46b5725d98 | |||
| 72542fa6f9 | |||
| a250d0461b | |||
| c7795bfc48 | |||
| 5de46b7e40 | |||
| 99c70872c1 | |||
| 3566669303 | |||
| 4557d05256 | |||
| fa421d165e | |||
| ecf20020d7 | |||
| ae85af61c7 | |||
| 659bbbf4fb | |||
| 427e2dddc4 | |||
| d47483f957 | |||
| 4bd8e26ae7 | |||
| 01f47282ed | |||
| afdd37cd97 | |||
| addaffcd1d | |||
| 63f7ce20a4 | |||
| f37c23db7a | |||
| d2bde0be21 | |||
| 9c446ebbd6 | |||
| 0f00fb9a27 | |||
| 2d6f819c86 | |||
| 56683ac409 | |||
| a72523c7df | |||
| 46eeb0bc22 | |||
| 6e8baef9b2 | |||
| 947b621733 | |||
| 4cc00bdaf2 | |||
| 59ef569a63 | |||
| abae225915 | |||
| 05bdf9daae | |||
| 0a8192168a | |||
| 88fd5a517e | |||
| 74c5b616a4 | |||
| 22a61d715b | |||
| a90ec7c64a | |||
| b22812b43a | |||
| 4c0da8c3d5 | |||
| 9aa30f77b7 | |||
| 2dbef83fa6 | |||
| 6ec7f789ef | |||
| 31c0afe29e | |||
| 46b07863c1 | |||
| 445e91e6b5 | |||
| 9daf386d66 | |||
| 49b4079cd8 | |||
| 0ffa1838a2 | |||
| 0efca29e95 | |||
| 0ab933efff | |||
| 4ee4a34323 | |||
| 985ccd6bba | |||
| 570e7528a7 | |||
| 918585968d | |||
| cf918a897f | |||
| 5fc27c1136 | |||
| 6bcc48c462 | |||
| 4dc368c7d0 | |||
| 17f99ed928 | |||
| 954d239b52 | |||
| ea167cbefc | |||
| 90a436236d | |||
| 4479a3fbd5 | |||
| f5216b77f8 | |||
| 39dc288978 | |||
| f37005958f | |||
| dfdc14ea86 | |||
| 82712776cc | |||
| 439446816c | |||
| 940108661c | |||
| 0423f33e93 | |||
| c2a4a7a6c2 | |||
| ff4e3dd976 | |||
| 94b00b4e7a | |||
| 48252c3c3d | |||
| 37adce2efb | |||
| 358cddd9a7 | |||
| 418df2fd93 | |||
| cd153c76f2 | |||
| f30c14b277 | |||
| 3a29812241 | |||
| 67e6b647a2 | |||
| ea0795c8a9 | |||
| 099acfca1d | |||
| 8969d11a22 | |||
| eedf32d197 | |||
| 9c8642593c | |||
| 8e89a2ef1b | |||
| 316c2fdd4d | |||
| 6bbf1d0996 | |||
| e854ba3c44 | |||
| f681c8963d | |||
| 8d5f22e43d | |||
| a62de839be | |||
| 5af60b2ff4 | |||
| c8d7fce938 | |||
| 90fbc790d9 | |||
| 1ce3fc972a | |||
| 9ea3f0f240 | |||
| 868fa90097 | |||
| 55bb20cf29 | |||
| 7c0671c81b | |||
| 12a66bd83e | |||
| 1efdcd7b10 | |||
| 2d9bcdb87a | |||
| ac9cace8f6 | |||
| f9bf27579e | |||
| 667cae2e62 | |||
| e8e0491cb5 | |||
| ee12f0bd18 | |||
| 5f24193c49 | |||
| dd29d37154 | |||
| b63e3aca00 | |||
| 859674ce7e | |||
| ea8b9ce462 | |||
| b5720bd14e | |||
| cc99409a7e | |||
| fef3e21c70 | |||
| f3d76c433a | |||
| 31d5715723 | |||
| 592eef3cda | |||
| 0541748e5f | |||
| cf1d9e8372 | |||
| b096d8869e | |||
| 2acc14b04a | |||
| 1f1efb0e17 | |||
| 1c08d854fb | |||
| 9c252fb226 | |||
| 4bda3b9e9b | |||
| 21da3c8602 | |||
| 7ec43776ae | |||
| 07d7e68dc2 | |||
| 8785c33d06 | |||
| 661ebe439d | |||
| 4732fa36a6 | |||
| 8dad158ae6 | |||
| bfc343d1ee | |||
| ca723c3b47 | |||
| b6657351fc | |||
| f26032ed7d | |||
| 012f7665aa | |||
| c28e8142f4 | |||
| 1462acbb92 | |||
| c52c659b94 | |||
| 6bf358fc66 | |||
| e8f57d3ace | |||
| 766e7193b9 | |||
| 6fe762aa7b | |||
| 7065b1b3ba | |||
| 72b812acad | |||
| d7ffa21fbe | |||
| c95d64909a | |||
| a5b2b04317 | |||
| 4705194a1d | |||
| 19572a674e | |||
| a1247f4d96 | |||
| 52412dfe31 | |||
| ab02e6e7c3 | |||
| 9ef99a2b92 | |||
| 4e5fac4b88 | |||
| 7a14aaa17e | |||
| 650863836c | |||
| 3a69ac23c0 | |||
| b873c6ae4d | |||
| 4835b1b897 | |||
| 67def6319e | |||
| c56d9ac7ce | |||
| e6588c4307 | |||
| 712389ab24 | |||
| 5f1be38490 | |||
| 7982592c6e | |||
| 69574918b5 | |||
| f6783e8f5e | |||
| 913b00a4d4 | |||
| 22fa132110 | |||
| 5a30fc0300 | |||
| c774ffc979 | |||
| c93e7fb9cd | |||
| 3437888964 | |||
| b314e0bdda | |||
| 2bdde6a528 | |||
| c61cb80a8b | |||
| 4217aab933 | |||
| ffb2956d90 | |||
| 9744547fab | |||
| b580d1cf5b | |||
| 855c7b608e | |||
| 707ed9a828 | |||
| a3ea514521 | |||
| 3dfaec5033 | |||
| 778106c41b | |||
| c47e9cdde4 | |||
| 8dd76420c8 | |||
| dfe026ac2d | |||
| f0849d0ed1 | |||
| b7ca898b77 | |||
| 1b8b377f90 | |||
| 7227f1ac78 | |||
| 23f088105e | |||
| 3bbb6b1058 | |||
| 3a4895b21c | |||
| 4e6afe9b64 | |||
| dd4c20249f | |||
| b54b2d47e9 | |||
| 64fb587d0f | |||
| 2ca3c65300 | |||
| 549f346d5e | |||
| 80a879bddf | |||
| 13c17a000a | |||
| 96dea75bc8 | |||
| 035fce6191 | |||
| 2f5dd171d0 | |||
| e7598d4340 | |||
| 76d0a39a0f | |||
| 3f25f072c6 | |||
| 5c75f249c7 | |||
| 02bfe63245 | |||
| faa205a486 | |||
| 3a6d645ea3 | |||
| e65468b97a | |||
| edbe6015f6 | |||
| f642e11a7a | |||
| fdc87fe296 | |||
| 7396c7595f | |||
| d39849ad00 | |||
| d65ee902f7 | |||
| eada4b0fc3 | |||
| 6f9619126a | |||
| 4bc6007a4d | |||
| d3f5154c19 | |||
| 71aa29cc71 | |||
| 98d8015015 | |||
| 42a44f210d | |||
| 29ff86b74f | |||
| 381afa2741 | |||
| 82d4099541 | |||
| 5f659c8de7 | |||
| e054ea6683 | |||
| 0deafb768b | |||
| cd612b452c | |||
| 009939f866 | |||
| cd006ce78e | |||
| 71bf2aa59f | |||
| 714b935474 | |||
| 2a2b83a695 | |||
| acdaa95854 | |||
| 278c5f6142 | |||
| 8ca3cd3155 | |||
| 2b7d7ce2cf | |||
| a22efc2eb6 | |||
| 804c8eff91 | |||
| aec8ebe237 | |||
| 7353790ed1 | |||
| e09e8e5823 | |||
| 92773417b3 | |||
| 12dc4fde8a | |||
| d1e5186d4a | |||
| d06ba95374 | |||
| f0e6c8ea8e | |||
| c94c204da2 | |||
| 71617a1c70 | |||
| 9f684937c6 | |||
| ff6665c716 | |||
| aa8fd3c861 | |||
| e2b42804d5 | |||
| 0f6e942c5b | |||
| c0a81978bf | |||
| b76c882210 | |||
| 14cbfbc5a4 | |||
| 9b1761f71f | |||
| 73ca2e0fa4 | |||
| 4e4c7f7d7b | |||
| 33f2c3f220 | |||
| dcca0133fb | |||
| 408201b69b | |||
| e024628d46 | |||
| 6428f29373 | |||
| cfaa6ab6df | |||
| 91ee7565ac | |||
| 1bbcac2ab3 | |||
| 60b2353afe | |||
| 50cfa4c971 | |||
| 691bf8faca | |||
| 89b4ae6a6f | |||
| 605a67932f | |||
| 1774dc74e3 | |||
| 2d641bf049 | |||
| 94a06d9b6f | |||
| a485942731 | |||
| 2c1b3b2480 | |||
| f1f7c70aee | |||
| 718caf8cb1 | |||
| 545cd4ec12 | |||
| 444ea9ec7e | |||
| 13609a5c44 | |||
| d6ad21f6f9 | |||
| 593a32a689 | |||
| 7fed5534eb | |||
| 1560284831 | |||
| 558a86739f | |||
| bfd60b733a | |||
| ae26512601 | |||
| 498c2f5256 | |||
| 459f12d61f | |||
| bcd6e537e0 | |||
| 867c3413e9 | |||
| 0f808d5c60 | |||
| f5767dd865 | |||
| 631a855bac | |||
| c616b5b03b | |||
| 3e9f4a5060 | |||
| 31434186ab | |||
| e81ba1c431 | |||
| b219ab607f | |||
| cd8307f432 | |||
| a720105208 | |||
| 34692c41f2 | |||
| 8a8b945eb8 | |||
| 99569c52ac | |||
| f696dc715b | |||
| 21b8552386 | |||
| 4b1886700d | |||
| a83ca1f85b | |||
| 65553c156c | |||
| 440357d2e8 | |||
| 5e62b9077a | |||
| 6192ced214 | |||
| ae31fee8dd | |||
| e7b00cc76e | |||
| 7d29c5d766 | |||
| 15776c4ce3 | |||
| d720179deb | |||
| 0d69e0ac97 | |||
| 09e267f8bc | |||
| 705ceca42a | |||
| 58722d372e | |||
| f482b8b95c | |||
| 21e4dee0e2 | |||
| c7a12ad75b | |||
| 7cd77ae379 | |||
| d59c99ddfe | |||
| 85760cc844 | |||
| 0692464b85 | |||
| 3968222744 | |||
| c05368d92e | |||
| 404136cd72 | |||
| b1d5ed0066 | |||
| e22474e0a7 | |||
| 45b7304630 | |||
| 692536c584 | |||
| 4e3d85887c | |||
| 87852328da | |||
| 2207cd7b52 | |||
| f3cd4e38d7 | |||
| 2dce1c0ad9 | |||
| 258ecad277 | |||
| 7f5092c0d5 | |||
| a7f315f596 | |||
| 4dc4aa122b | |||
| e3f388af11 | |||
| 83196628c9 | |||
| 315c07d4f6 | |||
| 47dadf0a43 | |||
| c62039f327 | |||
| a5ea560edc | |||
| e09e933f9c | |||
| dbd00d5a8b | |||
| 2da772df8e | |||
| 4cdad0e83e | |||
| 2f780da305 | |||
| 9d8066f4b8 | |||
| 2cc49256a9 | |||
| 79ab2e11f8 | |||
| c1f4302e62 | |||
| f66ec9168c | |||
| 37aecf72cb | |||
| 3c7be5e9be | |||
| 89dbae8f8e | |||
| 5eec8f754d | |||
| 49a82c8388 | |||
| 26bad4a1cd | |||
| fa0a38d3aa | |||
| b56fd4d29a | |||
| 4520747167 | |||
| e444ecb2c7 | |||
| 1336c47d18 | |||
| 519a74e8d2 | |||
| dea7714ef3 | |||
| 74b88b407e | |||
| 57c28c61e0 | |||
| 87320348dd | |||
| fcb70e69c1 | |||
| 4859629850 | |||
| bd0d248fd0 | |||
| c24a3edb44 | |||
| d0f197ea3d | |||
| 5502d93cd5 | |||
| 073fd76aa2 | |||
| 492a3f6928 | |||
| 1ce8fa8bdd | |||
| 1bb90233b9 | |||
| 49ce92f540 | |||
| 2107da2e1a | |||
| d98321026d | |||
| 0b6fabbc71 | |||
| bb0fad2834 | |||
| 08f9722e33 | |||
| 62ca30bbc6 | |||
| 467403362d | |||
| 265a78cd23 | |||
| a0e13d0481 | |||
| 77fc6435a0 | |||
| cd7e5bf020 | |||
| 5e7388f686 | |||
| 1047e751e6 | |||
| c598871764 | |||
| 2921c66834 | |||
| 7bbb083c36 | |||
| 531650c9f2 | |||
| f3098741c3 | |||
| 9dbffef140 | |||
| c97e504686 | |||
| 0c4b807caa | |||
| 1eca2b6cb0 | |||
| 17cbb3eab0 | |||
| c72f2a2c96 | |||
| 610f531653 | |||
| 1e9b28b369 | |||
| 6d4caca6cc | |||
| 15953f832f | |||
| d346ed0570 | |||
| df520a1134 | |||
| f90fc1d5ec | |||
| 797904afb5 | |||
| 489fbb5e42 | |||
| 9dd5a9ef89 | |||
| 02213802c5 | |||
| 8365855475 | |||
| 55eb86d2d5 | |||
| 0a9b944431 | |||
| f1e229193c | |||
| 04e309aeff | |||
| 99eb31b312 | |||
| 287275d886 | |||
| af6f8b5391 | |||
| 3b9b103c3f | |||
| 0c55ab9001 | |||
| 9c88f70740 | |||
| 9ab003df4c | |||
| 7f8d03d192 | |||
| c64ffde11f | |||
| 1568008c67 | |||
| 84211576e4 | |||
| 553f5b063f | |||
| f5d1f288d0 | |||
| a2df92dd68 | |||
| 1c6e51f8b2 | |||
| ef5da49bc0 | |||
| 7545625af4 | |||
| e3055b95f6 | |||
| 78eed8629c | |||
| cc29da8e81 | |||
| d1a6841c20 | |||
| 600842a1a6 | |||
| 8ba3e935a5 | |||
| 87504dd2cc | |||
| e4e6f37949 | |||
| 4b314e9d99 | |||
| a48ce296b8 | |||
| cf3e17c4b8 | |||
| bae36f9720 | |||
| 11db454b84 | |||
| 1b60c1d1a8 | |||
| 8321d5e380 | |||
| 64c9ce2ab0 | |||
| 08f9a90c95 | |||
| 9f06d6744e | |||
| f228a72118 | |||
| 3249a5e0bc | |||
| d1ac43de33 | |||
| 00f5e83506 | |||
| d3ecc1f640 | |||
| 902247fcdb | |||
| bd9e1096cc | |||
| db892adf34 | |||
| 1cbaad3ec9 | |||
| 401d08ea49 | |||
| ba17f7bc00 | |||
| db2b537380 | |||
| 06f26ef8f5 | |||
| a71bb7a532 | |||
| 99934b5100 | |||
| ff5f6fca3c | |||
| 3578f7d68f | |||
| 7bc01dcfa8 | |||
| bc7a748420 | |||
| c0194d025d | |||
| 19ca8a096b | |||
| cf5bfc297b | |||
| a17f9136dd | |||
| 7d07fb0b77 | |||
| 99935bada6 | |||
| be9dae57b9 | |||
| 4629043721 | |||
| 068234f0ca | |||
| ccb8ed1394 | |||
| 8d68d409eb | |||
| e842e022ba | |||
| 1e6b30343c | |||
| ddedb8d8a0 | |||
| 472aa52706 | |||
| ab97084058 | |||
| 0911b2dc2f | |||
| fd7d08cb10 | |||
| 3ae8359408 | |||
| 6b76a3c787 | |||
| 9fbf9ef667 | |||
| c8de7b97dd | |||
| b32dc17dd7 | |||
| 7c53b985cd | |||
| c8df26f227 | |||
| 3bc9671905 | |||
| 7fd959e6e3 | |||
| 24dc68652e | |||
| aad2d24c58 | |||
| 15d56abeb6 | |||
| d89ec67888 | |||
| f9f840fb9d | |||
| 8e8a2faa10 | |||
| 0c2784bb42 | |||
| c555cd0825 | |||
| 92e28326a4 | |||
| 2dbdd1f079 | |||
| fc58022d56 | |||
| ed9fe8a573 | |||
| 49e234d9f8 | |||
| a587ce88ea | |||
| a530371b6f | |||
| 0c5a83c021 | |||
| 12bb632371 | |||
| e4fc6774b1 | |||
| f57dfb3f2e | |||
| c3f7a049d8 | |||
| 3eee83c2f2 | |||
| ed8c662631 | |||
| 850df18d7c | |||
| 7f4da306c9 | |||
| 424a8ffaf4 | |||
| 8410e62531 | |||
| 533b686e0b | |||
| c6c54f90dc | |||
| 0f5fd4e48a | |||
| a7247e8a88 | |||
| c10a30346b | |||
| 436e86fdf2 | |||
| 29a137bde3 | |||
| f02a33ace3 | |||
| 9c34a3154d | |||
| ed385e8cf1 | |||
| 554e119eb9 | |||
| aad70c7199 | |||
| a051ce6cf4 | |||
| 40f19cce61 | |||
| b158858349 | |||
| d6737860bb | |||
| 123ba2baf1 | |||
| f2f8936cd1 | |||
| f0f5c26896 | |||
| 6c61ce52a3 | |||
| 1830c273c0 | |||
| 5efe4bd845 | |||
| 75a6714057 | |||
| 6339cc2088 | |||
| 98e4c4b48d | |||
| 9d5dda12be | |||
| 84b9f11120 | |||
| 2383c40105 | |||
| 305cf5379d | |||
| 9189f23e3e | |||
| 628263343b | |||
| 19cf7c9f14 | |||
| 363cd85ba6 | |||
| 7bb752715f | |||
| 9622c948c9 | |||
| baea5247f4 | |||
| 0ca2789a18 | |||
| 119709b844 | |||
| 1c914ff286 | |||
| 31a02a70a0 | |||
| 8e0df4c423 | |||
| 86a4ed7fb3 | |||
| 19c57258fe | |||
| fef3e55c55 | |||
| 6f973ca2af | |||
| 4b73d24d4b | |||
| 65818dccb1 | |||
| 7ac0b9f57c | |||
| 699f7f6716 | |||
| dae882d55c | |||
| c0a0bba5d8 | |||
| 52ba33c5a3 | |||
| 156c979842 | |||
| f846df5371 | |||
| 4a39af7cb3 | |||
| c7890f2468 | |||
| e1dfb4ee59 | |||
| 031582dd55 | |||
| 745c52e8db | |||
| cab3f5daa7 | |||
| 77cee15d64 | |||
| e7e7b664dd | |||
| db7f2cf548 | |||
| 58365a8907 | |||
| 172f7f7534 | |||
| 05e5c82c9b | |||
| 30fbf4bcfa | |||
| 9645f33c7b | |||
| 336d6ce189 | |||
| f283ef4342 | |||
| 41abbb12a0 | |||
| 89b80900f0 | |||
| f1539d2e37 | |||
| 84ef6d0049 | |||
| aa41a4d93b | |||
| 616752759b | |||
| dc004f37ee | |||
| cfbbd99bb8 | |||
| c74b7f20a5 | |||
| 9d22b3caa8 | |||
| f8039733cc | |||
| 87ca14afea | |||
| c134329ce9 | |||
| 2148d4b0f5 | |||
| 632cc3ec16 | |||
| bcfed5b362 | |||
| 4cb9f23a7d | |||
| 0aa7173e83 | |||
| b1b49e6195 | |||
| d23f4ac16a | |||
| 01da127c4e | |||
| ccb9c46005 | |||
| 01dfae4ac7 | |||
| 395cae439e | |||
| eb77aa51b2 | |||
| ac1d1a5858 | |||
| 74a72a5ce0 | |||
| a1bfdc6323 | |||
| 0720992998 | |||
| ef28875a8e | |||
| 10fb57f6fb | |||
| 7186a4a2d7 | |||
| ab4721a8b3 | |||
| 23c05bb5f6 | |||
| fe41174be8 | |||
| 513766d45b | |||
| 79ccaadaff | |||
| ecaa87e7ae | |||
| 0a08d8b653 | |||
| 7b18d3a3a8 | |||
| 058e4c9fd7 | |||
| 9d257cd115 | |||
| 1e5541175e | |||
| bae7f1b09c | |||
| e6e8f6367e | |||
| 3970fce503 | |||
| eda2272430 | |||
| 207ce6cbbe | |||
| 1d3e6ecbcf | |||
| a5a951cfa1 | |||
| a6f935ed68 | |||
| b09c7ba6b8 | |||
| ec66476ac6 | |||
| 008216d599 | |||
| 4a92275adb | |||
| 6fca311de0 | |||
| 8e279f55f1 | |||
| 2caac21b73 | |||
| c5d977baca | |||
| b5344f4577 | |||
| 0fa3842a70 | |||
| 6fc7c7a462 | |||
| 98dbd48890 | |||
| 55fc498359 | |||
| ba6dd081e9 | |||
| 7cdb0318b1 | |||
| 17d8516d3b | |||
| b78349aef7 | |||
| eb141ad12b | |||
| b446bf275c | |||
| b7109b1b2b | |||
| 3537f849ef | |||
| 9557f04fe7 | |||
| 1bb53329b5 | |||
| e026345a45 | |||
| 3492ec3517 | |||
| eb2a1734d3 | |||
| aa8a7c03c3 | |||
| 36ee994eb8 | |||
| 65544c64bf | |||
| b49acfdacf | |||
| 29d3ce15a8 | |||
| 84cb6b9569 | |||
| a848eab407 | |||
| e1f4955286 | |||
| ca21ca5d81 | |||
| 2399bfb784 | |||
| 11cee083ba | |||
| 6e66dc25c8 | |||
| 999793c944 | |||
| d111fd0680 | |||
| eed762df44 | |||
| d3dbdb24af | |||
| e585c4ec46 | |||
| 66421be942 | |||
| 9695291e37 | |||
| b65c386b6a | |||
| cd6f6b7a83 | |||
| cdaecb31df | |||
| 5169dc52dd | |||
| f72abb2867 | |||
| 2c0725390e | |||
| 5a940a3baf | |||
| 16b6345c7f | |||
| 5f0dbf856b | |||
| 2e9039da4e | |||
| 650cf31562 | |||
| 42b7843761 | |||
| d5f4266283 | |||
| 988ada3ce9 | |||
| 74562e6868 | |||
| dd2e7e879b | |||
| 365c1faf25 | |||
| 9797fe5b81 | |||
| 6d052d39ad | |||
| 2b242b9109 | |||
| 1c8741332e | |||
| a3ff03c8f3 | |||
| 3019ee5655 | |||
| 03ae3bcefa | |||
| 52deb48aac | |||
| 214866fcb9 | |||
| 7654661b77 | |||
| 51a9c741a5 | |||
| bce86d3211 | |||
| a4a8b05ef0 | |||
| 6116a75022 | |||
| 7243ab5cb6 | |||
| 43538902a3 | |||
| cf79a519d3 | |||
| d069928613 | |||
| dd13edf262 | |||
| 653bb2ac10 | |||
| cbdc2496fc | |||
| 3de04cb638 | |||
| 68d074aecf | |||
| 0df5aa80fe | |||
| 28bcad202a | |||
| 16d8d41baf | |||
| 7bd175220e | |||
| 28d348b364 | |||
| 91691fbd6a | |||
| 9856b6d3c5 | |||
| e1ca290424 | |||
| 0fa19fb0ac | |||
| ab7b6fc29f | |||
| f8acc31b0b | |||
| 4197365131 | |||
| 389caf1825 | |||
| bcadc28207 | |||
| 7e95758a6e | |||
| d44dd0de84 | |||
| 5cee498e1d | |||
| db1b027da2 | |||
| 7f4e641860 | |||
| 710245d597 | |||
| b689efcc9f | |||
| 89563b624b | |||
| 8c7140d6b8 | |||
| c892395089 | |||
| 4ce5cc8492 | |||
| 9b091b7c08 | |||
| 7b14e322d3 | |||
| 21acaa8d37 | |||
| bf42b97580 | |||
| c705f8cbff | |||
| f8c0449d4e | |||
| b28eaa1a94 | |||
| be509c7104 | |||
| 2931617202 | |||
| 82d0d622a8 | |||
| bcaca781b1 | |||
| ffedaea505 | |||
| aec986447e | |||
| b0e3cfedf4 | |||
| da41efa3db | |||
| 9e0cd2e14e | |||
| d1af751226 | |||
| d8b39c2698 | |||
| 618b014b3d | |||
| 4e990ae2b0 | |||
| 41ee57a6f7 | |||
| 7c353a6c6f | |||
| 72b2663949 | |||
| 727285e3df | |||
| a932c7c569 | |||
| 1272ae2d4d | |||
| ddaf0375dc | |||
| f60bb6b121 | |||
| fd3498add6 | |||
| 43de744c88 | |||
| 1ac2cdfc41 | |||
| 2dea6f55b5 | |||
| 76cf4f053f | |||
| eadf046b2c | |||
| e9591efd7e | |||
| fbf750f588 | |||
| 63c528dba9 | |||
| 0f27eba1b1 | |||
| a841d0ebe7 | |||
| 6a1717a624 | |||
| ee6508e93c | |||
| 5ad082627f | |||
| e0624ce986 | |||
| 995cf2ad42 | |||
| b9fffa149b | |||
| 7e8435cceb | |||
| 37071dbaf3 | |||
| 87c1231422 | |||
| d553157bb3 | |||
| 95f9ab035d | |||
| 21e11c1d47 | |||
| 6d59f77165 | |||
| 3a798cb21c | |||
| 1fc92d2a16 | |||
| 850bd96976 | |||
| 20b03aa755 | |||
| 5281eed1a5 | |||
| bedbf94b4a | |||
| 47917fd5c2 | |||
| 0abb51c87a | |||
| 557667b125 | |||
| 1d7bb43dfd | |||
| 6b3e8f083c | |||
| bc629337d6 | |||
| 58993b9799 | |||
| a3464d7184 | |||
| a8a31643f1 | |||
| df9ae295db | |||
| 8c348c935c | |||
| 3815d30fc1 | |||
| 778b9bb851 | |||
| 57dd9e6146 | |||
| 8eef4a9075 | |||
| 87f50ab513 | |||
| 2de86b6db5 | |||
| 9be62fb16e | |||
| f6f883b9d1 | |||
| 5de4d0792f | |||
| 8c43e7f7ce | |||
| 9e24a5abeb | |||
| b587b6d694 | |||
| bc3af8e64d | |||
| 7bd428cf6d | |||
| 912f3c8467 | |||
| a8febb2447 | |||
| 1b0b526822 | |||
| 6250ebdd75 | |||
| 156e4209f9 | |||
| 7492889f4e | |||
| 0c041120f6 | |||
| ecbb53aaea | |||
| ffe739b7eb | |||
| bd05be6fb6 | |||
| ff7df3818f | |||
| 39899e4edc | |||
| dc578f98e7 | |||
| d7ce41e724 | |||
| eaaeb2c843 | |||
| 0ae7c1efbf | |||
| cb13599739 | |||
| a9251de1dd | |||
| 56798e716a | |||
| 904f0a1197 | |||
| 4d3f2740ce | |||
| f9e7a29c08 | |||
| 6cb14a1c50 | |||
| ea9f933f9e | |||
| eb2e5e3da5 | |||
| 9a4e8bdb48 | |||
| fad0091d87 | |||
| b861151f63 | |||
| feeafc407a | |||
| ea1c1eb803 | |||
| b83e20b571 | |||
| 160ad64e67 | |||
| 1213ff26b4 | |||
| 3bf93f1f85 | |||
| a502581640 | |||
| 1ec041a48f | |||
| fdaf8492ef | |||
| 2433a7e89c | |||
| 6e358d3eab | |||
| 7e462e800f | |||
| 156e54c77f | |||
| 9752ec2655 | |||
| edc1e5fa84 | |||
| a959af087b | |||
| c5994f954a | |||
| 03a09debfa | |||
| 2cf6a9b691 | |||
| 5b712e8dbc | |||
| 5d7f2c706c | |||
| 22f51e1a28 | |||
| 61684ea73d | |||
| 45b37dcd10 | |||
| c0b2230b0b | |||
| 53fa8205f2 | |||
| ddbcf8bb19 | |||
| 6e50ddf5c8 | |||
| dda49f1501 | |||
| 4e2e5aa975 | |||
| 10fd3652d4 | |||
| 4d30510706 | |||
| ee74c4d870 | |||
| 45f5e0cb04 | |||
| 7d983f2847 | |||
| 3b2b5417aa | |||
| 0a0c4eac88 | |||
| d20cd43a99 | |||
| 7b4321ecee | |||
| 188bfd1a7e | |||
| 7b6825d85c | |||
| a27cb9c34e | |||
| 8445ebf107 | |||
| 6df315017c | |||
| 1931e78367 | |||
| 90692d89c5 | |||
| 4f02864e12 | |||
| 7562ec9e1a | |||
| 6d984c0407 | |||
| 3556e591b0 | |||
| 8fdb473d78 | |||
| 398cd15dcf | |||
| 5006b3e837 | |||
| 7216635d84 | |||
| 98ef7c39b7 | |||
| e039a5a9af | |||
| 412a185fa1 | |||
| 20bfe83849 | |||
| 39093130a3 | |||
| 9e9270a8b7 | |||
| 2c904c56f4 | |||
| a3b961e72f | |||
| 0e4c8df418 | |||
| 3b2d595a06 | |||
| b29223c5b6 | |||
| d805ec15b4 | |||
| d5cfdfb50d | |||
| 2e5d73a7e4 | |||
| 1c352cc81b | |||
| 9c6c18780f | |||
| cb23b65797 | |||
| 7a16814808 | |||
| 23553c9944 | |||
| 54fbe8a7c0 | |||
| ea01102f1d | |||
| 15e1e1dfab | |||
| 37ed9cd25b | |||
| 882af04027 | |||
| aa8a298ec7 | |||
| 05ab8fd868 | |||
| 1a736aadf5 | |||
| 2cea653249 | |||
| 1085238031 | |||
| 1356d91423 | |||
| fd2dc14ede | |||
| f91c5d3647 | |||
| 1f3aebf584 | |||
| b39d79a0f9 | |||
| c410914208 | |||
| a046f1caec | |||
| c25f918cc5 | |||
| 6fb1c8d3b9 | |||
| 6047a27c89 | |||
| 8446d016e4 | |||
| ab040d36d5 | |||
| a2781b5bd3 | |||
| a7903f31ac | |||
| c134837ca6 | |||
| f0a57e6714 | |||
| d9b0365c9f | |||
| 948f9456f5 | |||
| 32f94b463f | |||
| 035506496e | |||
| 477222a395 | |||
| 4bdfdfcb36 | |||
| 3167cd0817 | |||
| b52f35259b | |||
| 20bf85b14d | |||
| f02b99a1d0 | |||
| 9132ef57f1 | |||
| d42bf2d43c | |||
| 89042fbf4a | |||
| fc4dcd2db5 | |||
| 4e07d80ebc | |||
| c4a462d601 | |||
| 7fe46fb6f3 | |||
| 2cb94856fd | |||
| f53fc03c6c | |||
| 9278c51035 | |||
| ca9dc5f944 | |||
| f30cfb6166 | |||
| 9614929f77 | |||
| 8e418d4414 | |||
| 5e81bd833b | |||
| cc2ae80e7b | |||
| b921f6097d | |||
| 9679ef893b | |||
| e4fc3cebfd | |||
| 75135819cf | |||
| 072b6cce36 | |||
| 5d60f2cdf2 | |||
| c476593925 | |||
| 089467a419 | |||
| ee68e04f84 | |||
| 9cd46e74be | |||
| 5781220415 | |||
| 965fd66e0f | |||
| 7d3caa2875 | |||
| 418546a659 | |||
| d586c84b56 | |||
| 7b9e5fe99f | |||
| 648f0edc79 | |||
| 4d9d0884c3 | |||
| 19c0bbb94c | |||
| c6995c2283 | |||
| 49a263454c | |||
| 454519220f | |||
| 118fa98ca9 | |||
| 001fa639be | |||
| 009749cf8b | |||
| da94e7b1be | |||
| ea346145f6 | |||
| a01c379c95 | |||
| c9b781d5e1 | |||
| 0b49abf3f5 | |||
| e5fe4b0a99 | |||
| 7474d305fb | |||
| 4716929158 | |||
| 528ca09e8e | |||
| 233054219f | |||
| 0e2c4d4af1 | |||
| f101e9a775 | |||
| bf3863d1bf | |||
| 94e9291c40 | |||
| 459d133b5d | |||
| 773fad66bb | |||
| e250c73109 | |||
| 6870c05ffa | |||
| 199581bf74 | |||
| a9ae971602 | |||
| 913a5b5d0f | |||
| c594137c19 | |||
| fe03c85b97 | |||
| c338652045 | |||
| 1f5cdeb01b | |||
| e53a72c5f5 | |||
| fc2690888e | |||
| b4472fd7d4 | |||
| 796d02506a | |||
| 467f729f06 | |||
| 050ec8b988 | |||
| b2f41e95bf | |||
| e733c91783 | |||
| 4e982722a5 | |||
| 2b1f815c58 | |||
| 0fff1a6754 | |||
| d95d268ec5 | |||
| b4930bc40c | |||
| 5f302238ad | |||
| 7cba2c3f81 | |||
| 218c39b1ec | |||
| e915f4fcd7 | |||
| bc9164b952 | |||
| 7c34be267f | |||
| 042464438c | |||
| cf68e4722a | |||
| 7be37ff76e | |||
| 1118236075 | |||
| ef8a685621 | |||
| e4fef402c9 | |||
| 5528afa404 | |||
| e3189fae37 | |||
| 6d963b378c | |||
| 895ddfa38f | |||
| 28e334a0ba | |||
| d060f5b877 | |||
| 7b70d617e0 | |||
| 261ba4645d | |||
| 2ab7524e71 | |||
| d55969bc65 | |||
| 9976894091 | |||
| 76a0608a04 | |||
| 0d93d335a1 | |||
| ffb9788047 | |||
| 5dd82eede9 | |||
| 14b90466ef | |||
| 43592c3e49 | |||
| b46093b3cc | |||
| 2b9c092705 | |||
| bc73010d4e | |||
| 2d8d416483 | |||
| f549a0691e | |||
| 96c2fb6f54 | |||
| f6d475292c | |||
| bd4d4f49ff | |||
| 5a24eac375 | |||
| a684118dbb | |||
| a324376e60 | |||
| 04e50afaba | |||
| 69494c9212 | |||
| b2f0f02541 | |||
| 71f249113d | |||
| 1d84c00161 | |||
| 5bc7e19a28 | |||
| c46703a05d | |||
| 129a7c39af | |||
| ac3f3e8385 | |||
| bc8ad4df86 | |||
| 53ac3ec582 | |||
| d2d43711c2 | |||
| 510035b7b7 | |||
| c923d19bcc | |||
| bff9597360 | |||
| 78b39a60e8 | |||
| 932966b6b8 | |||
| 85a18e6d75 | |||
| 5d86bf9788 | |||
| 406d9878d8 | |||
| 4e6c5cb27a | |||
| a9c0185bf5 | |||
| 0111172a03 | |||
| 57541488d3 | |||
| 3782aa7d0a | |||
| d5740c1b08 | |||
| 3357979cc9 | |||
| 2f548dd9eb | |||
| f715d6ce68 | |||
| 350b605400 | |||
| 6a99d00487 | |||
| f87bf3f14d | |||
| 87347c0a04 | |||
| 217737b3e8 | |||
| efd8bd13da | |||
| 051bb63a81 | |||
| e4f4de6c8d | |||
| e6aa85e01d | |||
| cc4d94321e | |||
| c1292dff75 | |||
| 1d81cab4a1 | |||
| 2815cef1bb | |||
| d4523c4e53 | |||
| 5d894b6150 | |||
| 2eddbb9119 | |||
| a2513240ff | |||
| 0d509f94b7 | |||
| e4fbbcf2d6 | |||
| 9afacf72f8 | |||
| 78a7e65a2d | |||
| 3314de8dde | |||
| 8dab9b83be | |||
| 7b264ef2be | |||
| 5a6600f748 | |||
| 14ed0c3a3d | |||
| 229d78f1ef | |||
| f770e4ef42 | |||
| 66aa2f8f2a | |||
| 99d831c498 | |||
| a532287ddf | |||
| 7844e554aa | |||
| 10de4ff490 | |||
| eed65421ea | |||
| 7accc6e516 | |||
| fa8f9ccfaa | |||
| 8a264ca321 | |||
| f366eac7eb | |||
| 46d3992b41 | |||
| 164123f542 | |||
| 72c301dc54 | |||
| e8fd1e1dc7 | |||
| 2b7f8931a4 | |||
| ec3b71e5b6 | |||
| f7303881aa | |||
| 1b98be0b0a | |||
| e5b84d942e | |||
| 22e9ac22ba | |||
| 8a6cdec796 | |||
| c5c4499a55 | |||
| 30138f7a38 | |||
| 883429fa47 | |||
| 380ee75d9a | |||
| 99d9814083 | |||
| 141398aeac | |||
| 07cef6fd62 | |||
| aac041f960 | |||
| 6ef1600041 | |||
| 733836ea2d | |||
| b4767626d9 | |||
| 84edfac8fa | |||
| 69252ce721 | |||
| df1152516a | |||
| 02f9c25f52 | |||
| 7fb3988173 | |||
| a50352780b | |||
| ff093aeebb | |||
| aa691af664 | |||
| e0ffb01852 | |||
| 53be92c616 | |||
| 5f8f454b55 | |||
| 3700ecb717 | |||
| 811b0e7f30 | |||
| 08ea61f4df | |||
| 1d5368f5a2 | |||
| 90fb95e785 | |||
| 0f97f07461 | |||
| 7fe6b842a6 | |||
| 02a97bcb3a | |||
| fd4289aa64 | |||
| 716db8b964 | |||
| b55975a35a | |||
| 4cb34394b4 | |||
| aacb83290e | |||
| ddab4c269d | |||
| 6ddb0036fa | |||
| 0f7ca8cea4 | |||
| c4367e25a9 | |||
| e081aadd27 | |||
| 2bacab7dc0 | |||
| 824db6be2b | |||
| 2fdb26146b | |||
| b87d1a7fe1 | |||
| c242a62bcc | |||
| 6c91bce663 | |||
| 7fd46633a3 | |||
| 3c425990f6 | |||
| 55f16cc3ec | |||
| e01789bb0d | |||
| e3696af8ea | |||
| a26d7b5a8b | |||
| c4fe9150bf | |||
| ffc642048e | |||
| 8de6190a81 | |||
| d6e2c69202 | |||
| 1a85fa8e3c | |||
| c7b8508c6f | |||
| c35f6e2543 | |||
| 8052a90d04 | |||
| 3d20d7d330 | |||
| 5e722c6827 | |||
| 10198bc87f | |||
| a6b4043e8c | |||
| 9cea0163fa | |||
| 577176dd31 | |||
| a135eb44f3 | |||
| 252ed0e1e4 | |||
| fcb120a3ed | |||
| 988628a2e7 | |||
| c24cdf5cc1 | |||
| d748e004d2 | |||
| 380b73d1b9 | |||
| ef85e29a9b | |||
| 1daf5d1090 | |||
| 9d2c0e4aea | |||
| 7277ebb9c4 | |||
| c18074f6aa | |||
| 8a706193e7 | |||
| 9d77f5f5df | |||
| 6951d0e646 | |||
| 73dd55eac2 | |||
| b5a9389cc6 | |||
| d905c96fbe | |||
| 03f03f85af | |||
| 2b00a8a406 | |||
| f31c2dcd21 | |||
| 7699cfac02 | |||
| 5c48a457b4 | |||
| b5f439e1aa | |||
| 202fe5a0a9 | |||
| 68ccf57bd4 | |||
| 84970759d8 | |||
| 53cac58ebe | |||
| adc32ed319 | |||
| 7f0815a7b2 | |||
| 68f5565b63 | |||
| c552fb857d | |||
| 8a4ec47b92 | |||
| d281126337 | |||
| 4fb6ada5ec | |||
| ab924faa1e | |||
| b12b129af7 | |||
| 017c6b4d42 | |||
| d55cd5fbb4 | |||
| 13a6b1c8c6 | |||
| 0bc37e5d32 | |||
| 46c8572887 | |||
| e6b919007c | |||
| ac5a6096bb | |||
| db527fbbda | |||
| 8f201f1fee | |||
| d1e0762c12 | |||
| 010ae99c69 | |||
| a4632d6e86 | |||
| 2a3162f869 | |||
| 2063fd0742 | |||
| 128fe8a839 | |||
| 2478fc3a1b | |||
| 5a68d3bef7 | |||
| 195eeceb62 | |||
| ec8309af49 | |||
| 627a4dc802 | |||
| e2018535ef | |||
| ee56610f06 | |||
| fb9ff7748a | |||
| cdfb46d9d3 | |||
| 5545a94a9e | |||
| 9e811d959b | |||
| 957d5394d2 | |||
| 95afe1219e | |||
| 07937f1d71 | |||
| 671be0af0a | |||
| 7e9278f992 | |||
| 9194441c47 | |||
| 4f307c0085 | |||
| 3bdda55102 | |||
| b657b0fb1f | |||
| 4935ae6da5 | |||
| 72a50eae43 | |||
| fa37b90cff | |||
| 052a8db606 | |||
| 88ccba166e | |||
| 58998089a6 | |||
| f0a73b329e | |||
| c97678a477 | |||
| 2ad739275f | |||
| caad78885d | |||
| a234227b9f | |||
| 16f736e773 | |||
| 1fe24ad11d | |||
| 33c5e0827a | |||
| cbd36aeb8f | |||
| 2963bb3f41 | |||
| 09a6ceac63 | |||
| 75d8a01cab | |||
| 0968f75e9a | |||
| bc0db88afa | |||
| 5f11f12d0c | |||
| 2d18950542 | |||
| 50d314445a | |||
| f8d57d9265 | |||
| ebbe6db378 | |||
| 543cad3a23 | |||
| 41cc83dbf6 | |||
| ce7101be88 | |||
| 0a241e3a9c | |||
| 37857e88ea | |||
| 6fbcf46a8b | |||
| a36bc8f29a | |||
| 813b7bca6e | |||
| e2945f2a42 | |||
| eaf623a560 | |||
| 26aa504590 | |||
| e4dde3f4b6 | |||
| 9c90699c40 | |||
| b67cd50e8a | |||
| d2acaa03f5 | |||
| f2aa4cd10b | |||
| b940b99e75 | |||
| 868e0706ea | |||
| 324f544ba2 | |||
| 0a1981f90e | |||
| 0a36bfb088 | |||
| 0d94af099b | |||
| 4a6115ed64 | |||
| 42eb319751 | |||
| 343bddc6ad | |||
| 632a742ea9 | |||
| 08c9d1228a | |||
| 11d8b28fb4 | |||
| 12b6371209 | |||
| 24d22aaafb | |||
| ebf7822628 | |||
| 7669fa1582 | |||
| e49ba61917 | |||
| 96db1c2479 | |||
| 7abdb41a9e | |||
| 14f6d2f3b0 | |||
| 4e4b4975ad | |||
| 93fafcf886 | |||
| 82cc88a1b9 | |||
| 5c94e0bccc | |||
| 18a1788d2d | |||
| d0aa19118b | |||
| 039f6eebda | |||
| 8a464e8274 | |||
| 066b5a895d | |||
| 4c4af7928e | |||
| a0d8d9fcc6 | |||
| a2142b2ff8 | |||
| 5ed5ec5f7d | |||
| 95795620d5 | |||
| 890c0a9909 | |||
| b73cb40690 | |||
| ca448fc4d8 | |||
| 7a51c0d087 | |||
| 8762125bbf | |||
| 3d0c6e49ab | |||
| 03da35b29e | |||
| 3ccab60f43 | |||
| ed64b8c121 | |||
| 2a60ac47fe | |||
| 9cd72fe1e9 | |||
| 476b2c3735 | |||
| 5258c8363a | |||
| eb6d145dca | |||
| 63c003a02d | |||
| 5557fd814b | |||
| b1dc26ee06 | |||
| 56c0a588de | |||
| 47d65fcd87 | |||
| 85ab095630 | |||
| 1b5f0f6af6 | |||
| 2cf28eb2e7 | |||
| 2b300341a0 | |||
| 01fabca358 | |||
| 4ace552a27 | |||
| b61daaed33 | |||
| c6f0c89cf6 | |||
| 3c84975a09 | |||
| bb4ef4f0a2 | |||
| fd00330e6e | |||
| d10efec1ab | |||
| 3f08c99f19 | |||
| e3606d1b53 | |||
| ab593e3cd9 | |||
| 4a0aacf104 | |||
| 917cf20d37 | |||
| dabaeed8df | |||
| 4923b36e70 | |||
| ebf06fde65 | |||
| 438b3b50d9 |
@@ -0,0 +1,32 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
indent_style = tab
|
||||||
|
tab_width = 4
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
[*.bat]
|
||||||
|
end_of_line = crlf
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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,5 @@
|
|||||||
|
:exclamation: Please review the [guidelines for contributing](https://github.com/skylot/jadx/blob/master/CONTRIBUTING.md#Pull-Request-Process)
|
||||||
|
|
||||||
|
### Description
|
||||||
|
Please describe your pull request.
|
||||||
|
Reference issue it fixes.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
name: Build Artifacts
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master, build-test ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 11
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v3
|
||||||
|
with:
|
||||||
|
arguments: dist copyExe
|
||||||
|
|
||||||
|
- name: Save bundle artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ format('jadx-{0}', env.JADX_VERSION) }}
|
||||||
|
# Waiting fix for https://github.com/actions/upload-artifact/issues/39 to upload zip file
|
||||||
|
# Upload unpacked files for now
|
||||||
|
path: build/jadx/**/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Save exe artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ format('jadx-gui-{0}-no-jre-win.exe', env.JADX_VERSION) }}
|
||||||
|
path: build/*.exe
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
build-win-bundle:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: oracle-actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
release: 17
|
||||||
|
|
||||||
|
- name: Print Java version
|
||||||
|
shell: bash
|
||||||
|
run: java -version
|
||||||
|
|
||||||
|
- name: Set jadx version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
JADX_LAST_TAG=$(git describe --abbrev=0 --tags)
|
||||||
|
JADX_VERSION="${JADX_LAST_TAG:1}.$GITHUB_RUN_NUMBER-${GITHUB_SHA:0:8}"
|
||||||
|
echo "JADX_VERSION=$JADX_VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v3
|
||||||
|
with:
|
||||||
|
arguments: dist -PbundleJRE=true
|
||||||
|
|
||||||
|
- name: Save exe bundle artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ format('jadx-gui-{0}-with-jre-win', env.JADX_VERSION) }}
|
||||||
|
path: jadx-gui/build/*-with-jre-win/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 30
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
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@v4
|
||||||
|
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 11
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v3
|
||||||
|
with:
|
||||||
|
arguments: build dist copyExe
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
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@v4
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v3
|
||||||
|
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@v3
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
name: Validate Gradle Wrapper
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master, build-test ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validation:
|
||||||
|
name: Validation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
+15
-2
@@ -5,23 +5,36 @@
|
|||||||
|
|
||||||
# IntelliJ Idea files
|
# IntelliJ Idea files
|
||||||
.idea/
|
.idea/
|
||||||
|
.run/
|
||||||
out/
|
out/
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
|
.attach_pid*
|
||||||
|
*.hprof
|
||||||
|
|
||||||
|
**/.DS_Store
|
||||||
|
|
||||||
bin/
|
bin/
|
||||||
target/
|
target/
|
||||||
build/
|
build/
|
||||||
|
classes/
|
||||||
idea/
|
idea/
|
||||||
.gradle/
|
.gradle/
|
||||||
gradle.properties
|
node_modules/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
jadx-output/
|
||||||
*-tmp/
|
*-tmp/
|
||||||
|
**/tmp/
|
||||||
|
*.jobf
|
||||||
|
|
||||||
*.dex
|
|
||||||
*.class
|
*.class
|
||||||
*.dump
|
*.dump
|
||||||
*.log
|
*.log
|
||||||
*.cfg
|
*.cfg
|
||||||
|
*.orig
|
||||||
|
quark.json
|
||||||
|
|
||||||
|
cliff.toml
|
||||||
|
jadx-gui/src/main/resources/logback.xml
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
variables:
|
||||||
|
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
|
||||||
|
TERM: "dumb"
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- chmod +x gradlew
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- test
|
||||||
|
|
||||||
|
java-11:
|
||||||
|
stage: test
|
||||||
|
image: eclipse-temurin:11
|
||||||
|
script: ./gradlew clean build dist copyExe
|
||||||
|
|
||||||
|
java-17:
|
||||||
|
stage: test
|
||||||
|
image: eclipse-temurin:17
|
||||||
|
script: ./gradlew clean build dist copyExe
|
||||||
|
|
||||||
|
java-21:
|
||||||
|
stage: test
|
||||||
|
image: eclipse-temurin:21
|
||||||
|
script: ./gradlew clean build dist copyExe
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
jdk:
|
||||||
|
- openjdk11
|
||||||
|
install:
|
||||||
|
- echo "Jitpack is not supported. Use artifacts from Maven Central (https://search.maven.org/search?q=jadx), check usage help at https://github.com/skylot/jadx/wiki/Use-jadx-as-a-library"
|
||||||
|
- ./gradlew intentional-fail
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="jadx-gui" type="Application" factoryName="Application">
|
||||||
|
<option name="MAIN_CLASS_NAME" value="jadx.gui.JadxGUI"/>
|
||||||
|
<module name="jadx.jadx-gui.main"/>
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="-v"/>
|
||||||
|
<option name="VM_PARAMETERS"
|
||||||
|
value="-Xms128M -XX:MaxRAMPercentage=70.0 -Dawt.useSystemAAFontSettings=lcd -Dswing.aatext=true -Djava.util.Arrays.useLegacyMergeSort=true -Djdk.util.zip.disableZip64ExtraFieldValidation=true -XX:+IgnoreUnrecognizedVMOptions --add-opens=java.base/java.lang=ALL-UNNAMED"/>
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true"/>
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
language: java
|
|
||||||
jdk:
|
|
||||||
- oraclejdk7
|
|
||||||
- openjdk7
|
|
||||||
- openjdk6
|
|
||||||
before_install:
|
|
||||||
- chmod +x gradlew
|
|
||||||
script:
|
|
||||||
- TERM=dumb ./gradlew clean build dist
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
- skylot@gmail.com
|
|
||||||
+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",
|
||||||
|
]
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## 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
|
||||||
|
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
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
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
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
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:
|
||||||
|
- 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:
|
||||||
|
- 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 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 or dependencies updates changes.
|
||||||
|
|
||||||
|
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 are passing build: `./gradlew clean build dist`
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
The majority of jadx is written and copyrighted by me (Skylot)
|
The majority of jadx is written and copyrighted by me (Skylot)
|
||||||
and released under the Apache 2.0 license:
|
and released under the Apache 2.0 license (see LICENSE file for full license text):
|
||||||
|
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
Copyright 2013, Skylot
|
Copyright 2015, Skylot
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,8 +18,8 @@ limitations under the License.
|
|||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
|
|
||||||
|
|
||||||
Various portions of the code including dx library are taken from
|
Various portions of the code including dx library are taken from
|
||||||
the Android Open Source Project, and are used in accordance with
|
the Android Open Source Project, and are used in accordance with
|
||||||
the following license:
|
the following license:
|
||||||
|
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
@@ -74,10 +74,10 @@ Copyright (c) 2004-2011 QOS.ch
|
|||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
the following conditions:
|
the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
The above copyright notice and this permission notice shall be
|
||||||
included in all copies or substantial portions of the Software.
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
@@ -92,24 +92,60 @@ Logback source code and binaries are dual-licensed under the EPL v1.0 and the LG
|
|||||||
|
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
Logback: the reliable, generic, fast and flexible logging framework.
|
Logback: the reliable, generic, fast and flexible logging framework.
|
||||||
Copyright (C) 1999-2012, QOS.ch. All rights reserved.
|
Copyright (C) 1999-2012, QOS.ch. All rights reserved.
|
||||||
|
|
||||||
This program and the accompanying materials are dual-licensed under
|
This program and the accompanying materials are dual-licensed under
|
||||||
either the terms of the Eclipse Public License v1.0 as published by
|
either the terms of the Eclipse Public License v1.0 as published by
|
||||||
the Eclipse Foundation
|
the Eclipse Foundation
|
||||||
|
|
||||||
or (per the licensee's choosing)
|
or (per the licensee's choosing)
|
||||||
|
|
||||||
under the terms of the GNU Lesser General Public License version 2.1
|
under the terms of the GNU Lesser General Public License version 2.1
|
||||||
as published by the Free Software Foundation.
|
as published by the Free Software Foundation.
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
ASM library:
|
||||||
|
|
||||||
|
*******************************************************************************
|
||||||
|
Copyright (c) 2000-2011 INRIA, France Telecom
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holders nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*******************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Jadx-gui components
|
Jadx-gui components
|
||||||
===================
|
===================
|
||||||
|
|
||||||
RSyntaxTextArea library licensed under modified BSD liense:
|
RSyntaxTextArea library (https://github.com/bobbylight/RSyntaxTextArea)
|
||||||
|
licensed under modified BSD license:
|
||||||
|
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
Copyright (c) 2012, Robert Futrell
|
Copyright (c) 2012, Robert Futrell
|
||||||
@@ -139,7 +175,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
Concurrent Trees (https://code.google.com/p/concurrent-trees/)
|
||||||
|
licenced under Apache License 2.0:
|
||||||
|
|
||||||
|
*******************************************************************************
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*******************************************************************************
|
||||||
|
|
||||||
|
|
||||||
|
Image Viewer (https://github.com/kazocsaba/imageviewer)
|
||||||
|
|
||||||
|
*******************************************************************************
|
||||||
|
Copyright (c) 2008-2012 Kazó Csaba
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*******************************************************************************
|
||||||
|
|
||||||
|
JFontChooser Component - http://sourceforge.jp/projects/jfontchooser/
|
||||||
|
|
||||||
Icons copied from several places:
|
Icons copied from several places:
|
||||||
- Eclipse Project (JDT UI) - licensed under EPL v1.0 (http://www.eclipse.org/legal/epl-v10.html)
|
- Eclipse Project (JDT UI) - licensed under EPL v1.0 (http://www.eclipse.org/legal/epl-v10.html)
|
||||||
- famfamfam silk icon set (http://www.famfamfam.com/lab/icons/silk/) - licensed under Creative Commons Attribution 2.5 License (http://creativecommons.org/licenses/by/2.5/)
|
- famfamfam silk icon set (http://www.famfamfam.com/lab/icons/silk/) - licensed
|
||||||
|
under Creative Commons Attribution 2.5 License (http://creativecommons.org/licenses/by/2.5/)
|
||||||
|
|||||||
@@ -1,54 +1,201 @@
|
|||||||
## JADX
|
<img src="https://raw.githubusercontent.com/skylot/jadx/master/jadx-gui/src/main/resources/logos/jadx-logo.png" width="64" align="left" />
|
||||||
|
|
||||||
|
## JADX
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
[](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
|
**jadx** - Dex to Java decompiler
|
||||||
|
|
||||||
Command line and GUI tools for produce Java source code from Android Dex and Apk files
|
Command line and GUI tools for producing Java source code from Android Dex and Apk files
|
||||||
|
|
||||||
Note: jadx-gui now in experimental stage
|
: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
|
||||||
|
|
||||||
|
**Main features:**
|
||||||
|
- decompile Dalvik bytecode to java classes from APK, dex, aar, aab and zip files
|
||||||
|
- decode `AndroidManifest.xml` and other resources from `resources.arsc`
|
||||||
|
- deobfuscator included
|
||||||
|
|
||||||
### Downloads
|
**jadx-gui features:**
|
||||||
- [unstable](https://drone.io/github.com/skylot/jadx/files)
|
- view decompiled code with highlighted syntax
|
||||||
[](https://drone.io/github.com/skylot/jadx/latest)
|
- jump to declaration
|
||||||
[](https://travis-ci.org/skylot/jadx)
|
- find usage
|
||||||
- from [github](https://github.com/skylot/jadx/releases)
|
- full text search
|
||||||
- from [sourceforge](http://sourceforge.net/projects/jadx/files/)
|
- smali debugger, check [wiki page](https://github.com/skylot/jadx/wiki/Smali-debugger) for setup and usage
|
||||||
|
|
||||||
|
Jadx-gui key bindings can be found [here](https://github.com/skylot/jadx/wiki/JADX-GUI-Key-bindings)
|
||||||
|
|
||||||
|
See these features in action here: [jadx-gui features overview](https://github.com/skylot/jadx/wiki/jadx-gui-features-overview)
|
||||||
|
|
||||||
|
<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-artifacts/master)
|
||||||
|
|
||||||
|
After download unpack zip file go to `bin` directory and run:
|
||||||
|
- `jadx` - command line version
|
||||||
|
- `jadx-gui` - UI version
|
||||||
|
|
||||||
|
On Windows run `.bat` files with double-click\
|
||||||
|
**Note:** ensure you have installed Java 11 or later 64-bit version.
|
||||||
|
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
|
||||||
|
```
|
||||||
|
3. [Flathub ](https://flathub.org/apps/details/com.github.skylot.jadx)
|
||||||
|
```bash
|
||||||
|
flatpak install flathub com.github.skylot.jadx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use jadx as a library
|
||||||
|
You can use jadx in your java projects, check details on [wiki page](https://github.com/skylot/jadx/wiki/Use-jadx-as-a-library)
|
||||||
|
|
||||||
|
### Build from source
|
||||||
|
JDK 11 or higher must be installed:
|
||||||
|
```
|
||||||
|
git clone https://github.com/skylot/jadx.git
|
||||||
|
cd jadx
|
||||||
|
./gradlew dist
|
||||||
|
```
|
||||||
|
|
||||||
### Building from source
|
|
||||||
git clone https://github.com/skylot/jadx.git
|
|
||||||
cd jadx
|
|
||||||
./gradlew dist
|
|
||||||
|
|
||||||
(on Windows, use `gradlew.bat` instead of `./gradlew`)
|
(on Windows, use `gradlew.bat` instead of `./gradlew`)
|
||||||
|
|
||||||
Scripts for run jadx will be placed in `build/jadx/bin`
|
Scripts for run jadx will be placed in `build/jadx/bin`
|
||||||
and also packed to `build/jadx-<version>.zip`
|
and also packed to `build/jadx-<version>.zip`
|
||||||
|
|
||||||
|
|
||||||
### Run
|
|
||||||
Run **jadx** on itself:
|
|
||||||
|
|
||||||
cd build/jadx/
|
|
||||||
bin/jadx -d out lib/jadx-core-*.jar
|
|
||||||
#or
|
|
||||||
bin/jadx-gui lib/jadx-core-*.jar
|
|
||||||
|
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
```
|
```
|
||||||
jadx[-gui] [options] <input file> (.dex, .apk or .jar)
|
jadx[-gui] [command] [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk)
|
||||||
|
commands (use '<command> --help' for command options):
|
||||||
|
plugins - manage jadx plugins
|
||||||
|
|
||||||
options:
|
options:
|
||||||
-d, --output-dir - output directory
|
-d, --output-dir - output directory
|
||||||
-j, --threads-count - processing threads count
|
-ds, --output-dir-src - output directory for sources
|
||||||
-f, --fallback - make simple dump (using goto instead of 'if', 'for', etc)
|
-dr, --output-dir-res - output directory for resources
|
||||||
--cfg - save methods control flow graph to dot file
|
-r, --no-res - do not decode resources
|
||||||
--raw-cfg - save methods control flow graph (use raw instructions)
|
-s, --no-src - do not decompile source code
|
||||||
-v, --verbose - verbose output
|
--single-class - decompile a single class, full name, raw or alias
|
||||||
-h, --help - print this help
|
--single-class-output - file or dir for write if decompile a single class
|
||||||
Example:
|
--output-format - can be 'java' or 'json', default: java
|
||||||
jadx -d out classes.dex
|
-e, --export-gradle - save as android gradle project
|
||||||
|
-j, --threads-count - processing threads count, default: 4
|
||||||
|
-m, --decompilation-mode - code output mode:
|
||||||
|
'auto' - trying best options (default)
|
||||||
|
'restructure' - restore code structure (normal java code)
|
||||||
|
'simple' - simplified instructions (linear, with goto's)
|
||||||
|
'fallback' - raw instructions without modifications
|
||||||
|
--show-bad-code - show inconsistent code (incorrectly decompiled)
|
||||||
|
--no-xml-pretty-print - do not prettify XML
|
||||||
|
--no-imports - disable use of imports, always write entire package name
|
||||||
|
--no-debug-info - disable debug info parsing and processing
|
||||||
|
--add-debug-lines - add comments with debug line numbers if available
|
||||||
|
--no-inline-anonymous - disable anonymous classes inline
|
||||||
|
--no-inline-methods - disable methods inline
|
||||||
|
--no-move-inner-classes - disable move inner classes into parent
|
||||||
|
--no-inline-kotlin-lambda - disable inline for Kotlin lambdas
|
||||||
|
--no-finally - don't extract finally block
|
||||||
|
--no-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-use-sourcename - use source file name as class name alias
|
||||||
|
--deobf-res-name-source - better name source for resources:
|
||||||
|
'auto' - automatically select best name (default)
|
||||||
|
'resources' - use resources names
|
||||||
|
'code' - use R class fields names
|
||||||
|
--use-kotlin-methods-for-var-names - use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide, default: apply
|
||||||
|
--rename-flags - fix options (comma-separated list of):
|
||||||
|
'case' - fix case sensitivity issues (according to --fs-case-sensitive option),
|
||||||
|
'valid' - rename java identifiers to make them valid,
|
||||||
|
'printable' - remove non-printable chars from identifiers,
|
||||||
|
or single 'none' - to disable all renames
|
||||||
|
or single 'all' - to enable all (default)
|
||||||
|
--integer-format - how integers are displayed:
|
||||||
|
'auto' - automatically select (default)
|
||||||
|
'decimal' - use decimal
|
||||||
|
'hexadecimal' - use hexadecimal
|
||||||
|
--fs-case-sensitive - treat filesystem as case sensitive, false by default
|
||||||
|
--cfg - save methods control flow graph to dot file
|
||||||
|
--raw-cfg - save methods control flow graph (use raw instructions)
|
||||||
|
-f, --fallback - set '--decompilation-mode' to 'fallback' (deprecated)
|
||||||
|
--use-dx - use dx/d8 to convert java bytecode
|
||||||
|
--comments-level - set code comments level, values: error, warn, info, debug, user-only, none, default: info
|
||||||
|
--log-level - set log level, values: quiet, progress, error, warn, info, debug, default: progress
|
||||||
|
-v, --verbose - verbose output (set --log-level to DEBUG)
|
||||||
|
-q, --quiet - turn off output (set --log-level to QUIET)
|
||||||
|
--version - print jadx version
|
||||||
|
-h, --help - print this help
|
||||||
|
|
||||||
|
Plugin options (-P<name>=<value>):
|
||||||
|
1) dex-input: Load .dex and .apk files
|
||||||
|
- dex-input.verify-checksum - verify dex file checksum before load, values: [yes, no], default: yes
|
||||||
|
2) java-convert: Convert .class, .jar and .aar files to dex
|
||||||
|
- java-convert.mode - convert mode, values: [dx, d8, both], default: both
|
||||||
|
- java-convert.d8-desugar - use desugar in d8, values: [yes, no], default: no
|
||||||
|
3) kotlin-metadata: Use kotlin.Metadata annotation for code generation
|
||||||
|
- kotlin-metadata.class-alias - rename class alias, values: [yes, no], default: yes
|
||||||
|
- kotlin-metadata.method-args - rename function arguments, values: [yes, no], default: yes
|
||||||
|
- kotlin-metadata.fields - rename fields, values: [yes, no], default: yes
|
||||||
|
- kotlin-metadata.companion - rename companion object, values: [yes, no], default: yes
|
||||||
|
- kotlin-metadata.data-class - add data class modifier, values: [yes, no], default: yes
|
||||||
|
- kotlin-metadata.to-string - rename fields using toString, values: [yes, no], default: yes
|
||||||
|
- kotlin-metadata.getters - rename simple getters to field names, values: [yes, no], default: yes
|
||||||
|
4) rename-mappings: various mappings support
|
||||||
|
- rename-mappings.format - mapping format, values: [auto, TINY, TINY_2, ENIGMA, ENIGMA_DIR, MCP, SRG, TSRG, TSRG2, PROGUARD], default: auto
|
||||||
|
- rename-mappings.invert - invert mapping, values: [yes, no], default: no
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
JADX_DISABLE_ZIP_SECURITY - set to 'true' to disable all security checks for zip files
|
||||||
|
JADX_ZIP_MAX_ENTRIES_COUNT - maximum allowed number of entries in zip files (default: 100 000)
|
||||||
|
JADX_TMP_DIR - custom temp directory, using system by default
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
jadx -d out classes.dex
|
||||||
|
jadx --rename-flags "none" classes.dex
|
||||||
|
jadx --rename-flags "valid, printable" classes.dex
|
||||||
|
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
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
Please check wiki page [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A)
|
||||||
|
|
||||||
|
### Contributing
|
||||||
|
To support this project you can:
|
||||||
|
- Post thoughts about new features/optimizations that important to you
|
||||||
|
- Submit decompilation issues, please read before proceed: [Open issue](CONTRIBUTING.md#Open-Issue)
|
||||||
|
- Open pull request, please follow these rules: [Pull Request Process](CONTRIBUTING.md#Pull-Request-Process)
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
*Licensed under the Apache 2.0 License*
|
*Licensed under the Apache 2.0 License*
|
||||||
|
|
||||||
*Copyright 2013 by Skylot*
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## 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.
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
ext.jadxVersion = file('version').readLines().get(0)
|
|
||||||
|
|
||||||
apply plugin: 'sonar-runner'
|
|
||||||
|
|
||||||
subprojects {
|
|
||||||
apply plugin: 'java'
|
|
||||||
apply plugin: 'idea'
|
|
||||||
apply plugin: 'eclipse'
|
|
||||||
|
|
||||||
sourceCompatibility = 1.6
|
|
||||||
targetCompatibility = 1.6
|
|
||||||
|
|
||||||
version = jadxVersion
|
|
||||||
|
|
||||||
gradle.projectsEvaluated {
|
|
||||||
tasks.withType(Compile) {
|
|
||||||
if (!"${it}".contains(":jadx-samples:")) {
|
|
||||||
options.compilerArgs << "-Xlint" << "-Xlint:unchecked" << "-Xlint:deprecation"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
version = jadxVersion
|
|
||||||
manifest {
|
|
||||||
mainAttributes('jadx-version' : jadxVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile 'org.slf4j:slf4j-api:1.7.5'
|
|
||||||
testCompile 'junit:junit:4.11'
|
|
||||||
testCompile "org.mockito:mockito-core:1.9.5"
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task copyArtifacts(type: Sync, dependsOn: ['jadx-cli:installApp', 'jadx-gui:installApp']) {
|
|
||||||
destinationDir file("$buildDir/jadx")
|
|
||||||
['jadx-cli', 'jadx-gui'].each {
|
|
||||||
from tasks.getByPath(":${it}:installApp").destinationDir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task pack(type: Zip, dependsOn: copyArtifacts) {
|
|
||||||
destinationDir buildDir
|
|
||||||
archiveName "jadx-${jadxVersion}.zip"
|
|
||||||
from copyArtifacts.destinationDir
|
|
||||||
}
|
|
||||||
|
|
||||||
task dist(dependsOn: pack) {
|
|
||||||
description = 'Build jadx distribution zip'
|
|
||||||
}
|
|
||||||
|
|
||||||
task samples(dependsOn: 'jadx-samples:samples') {
|
|
||||||
}
|
|
||||||
|
|
||||||
task build(dependsOn: [dist, samples]) {
|
|
||||||
}
|
|
||||||
|
|
||||||
task clean(type: Delete) {
|
|
||||||
delete buildDir
|
|
||||||
}
|
|
||||||
|
|
||||||
task wrapper(type: Wrapper) {
|
|
||||||
gradleVersion = '1.9'
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
import com.diffplug.gradle.spotless.FormatExtension
|
||||||
|
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||||
|
import com.diffplug.spotless.LineEnding
|
||||||
|
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
|
||||||
|
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("com.github.ben-manes.versions") version "0.51.0"
|
||||||
|
id("se.patrikerdes.use-latest-versions") version "0.2.18"
|
||||||
|
id("com.diffplug.spotless") version "6.25.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
val jadxVersion by extra { System.getenv("JADX_VERSION") ?: "dev" }
|
||||||
|
println("jadx version: $jadxVersion")
|
||||||
|
version = jadxVersion
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
apply(plugin = "java")
|
||||||
|
apply(plugin = "checkstyle")
|
||||||
|
apply(plugin = "com.diffplug.spotless")
|
||||||
|
apply(plugin = "com.github.ben-manes.versions")
|
||||||
|
apply(plugin = "se.patrikerdes.use-latest-versions")
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
configure<SpotlessExtension> {
|
||||||
|
java {
|
||||||
|
importOrderFile("$rootDir/config/code-formatter/eclipse.importorder")
|
||||||
|
eclipse().configFile("$rootDir/config/code-formatter/eclipse.xml")
|
||||||
|
removeUnusedImports()
|
||||||
|
commonFormatOptions()
|
||||||
|
}
|
||||||
|
kotlin {
|
||||||
|
ktlint().editorConfigOverride(mapOf("indent_style" to "tab"))
|
||||||
|
commonFormatOptions()
|
||||||
|
}
|
||||||
|
kotlinGradle {
|
||||||
|
ktlint()
|
||||||
|
commonFormatOptions()
|
||||||
|
}
|
||||||
|
format("misc") {
|
||||||
|
target("**/*.gradle", "**/*.xml", "**/.gitignore", "**/.properties")
|
||||||
|
targetExclude(".gradle/**", ".idea/**", "*/build/**")
|
||||||
|
commonFormatOptions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named<DependencyUpdatesTask>("dependencyUpdates") {
|
||||||
|
rejectVersionIf {
|
||||||
|
// disallow release candidates as upgradable versions from stable versions
|
||||||
|
isNonStable(candidate.version) && !isNonStable(currentVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FormatExtension.commonFormatOptions() {
|
||||||
|
lineEndings = LineEnding.UNIX
|
||||||
|
encoding = Charsets.UTF_8
|
||||||
|
trimTrailingWhitespace()
|
||||||
|
endWithNewline()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isNonStable(version: String): Boolean {
|
||||||
|
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase(Locale.getDefault()).contains(it) }
|
||||||
|
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
|
||||||
|
val isStable = stableKeyword || regex.matches(version)
|
||||||
|
return isStable.not()
|
||||||
|
}
|
||||||
|
|
||||||
|
val copyArtifacts by tasks.registering(Copy::class) {
|
||||||
|
val jarCliPattern = "jadx-cli-(.*)-all.jar".toPattern()
|
||||||
|
from(tasks.getByPath(":jadx-cli:installShadowDist")) {
|
||||||
|
exclude("**/*.jar")
|
||||||
|
filter { line ->
|
||||||
|
jarCliPattern.matcher(line).replaceAll("jadx-$1-all.jar")
|
||||||
|
.replace("-jar \"\\\"\$CLASSPATH\\\"\"", "-cp \"\\\"\$CLASSPATH\\\"\" jadx.cli.JadxCLI")
|
||||||
|
.replace("-jar \"%CLASSPATH%\"", "-cp \"%CLASSPATH%\" jadx.cli.JadxCLI")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jarGuiPattern = "jadx-gui-(.*)-all.jar".toPattern()
|
||||||
|
from(tasks.getByPath(":jadx-gui:installShadowDist")) {
|
||||||
|
exclude("**/*.jar")
|
||||||
|
filter { line -> jarGuiPattern.matcher(line).replaceAll("jadx-$1-all.jar") }
|
||||||
|
}
|
||||||
|
from(tasks.getByPath(":jadx-gui:installShadowDist")) {
|
||||||
|
include("**/*.jar")
|
||||||
|
rename("jadx-gui-(.*)-all.jar", "jadx-$1-all.jar")
|
||||||
|
}
|
||||||
|
into(layout.buildDirectory.dir("jadx"))
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
|
||||||
|
val pack by tasks.registering(Zip::class) {
|
||||||
|
from(copyArtifacts)
|
||||||
|
archiveFileName.set("jadx-$jadxVersion.zip")
|
||||||
|
destinationDirectory.set(layout.buildDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
val copyExe by tasks.registering(Copy::class) {
|
||||||
|
group = "jadx"
|
||||||
|
description = "Copy exe to build dir"
|
||||||
|
|
||||||
|
// next task dependencies not needed, but gradle throws warning because of same output dir
|
||||||
|
mustRunAfter("jar")
|
||||||
|
mustRunAfter(pack)
|
||||||
|
|
||||||
|
from(tasks.getByPath("jadx-gui:createExe"))
|
||||||
|
include("*.exe")
|
||||||
|
into(layout.buildDirectory)
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
|
||||||
|
val distWinBundle by tasks.registering(Copy::class) {
|
||||||
|
group = "jadx"
|
||||||
|
description = "Copy bundle to build dir"
|
||||||
|
|
||||||
|
dependsOn(tasks.getByPath(":jadx-gui:distWinWithJre"))
|
||||||
|
|
||||||
|
// next task dependencies not needed, but gradle throws warning because of same output dir
|
||||||
|
mustRunAfter("jar")
|
||||||
|
mustRunAfter(pack)
|
||||||
|
|
||||||
|
from(tasks.getByPath("jadx-gui:distWinWithJre").outputs) {
|
||||||
|
include("*.zip")
|
||||||
|
}
|
||||||
|
into(layout.buildDirectory)
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
|
||||||
|
val dist by tasks.registering {
|
||||||
|
group = "jadx"
|
||||||
|
description = "Build jadx distribution zip"
|
||||||
|
|
||||||
|
dependsOn(pack)
|
||||||
|
|
||||||
|
val os = DefaultNativePlatform.getCurrentOperatingSystem()
|
||||||
|
if (os.isWindows) {
|
||||||
|
if (project.hasProperty("bundleJRE")) {
|
||||||
|
println("Build win bundle with JRE")
|
||||||
|
dependsOn(distWinBundle)
|
||||||
|
} else {
|
||||||
|
dependsOn(copyExe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cleanBuildDir by tasks.registering(Delete::class) {
|
||||||
|
group = "jadx"
|
||||||
|
delete(layout.buildDirectory)
|
||||||
|
}
|
||||||
|
tasks.getByName("clean").dependsOn(cleanBuildDir)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
plugins {
|
||||||
|
`kotlin-dsl`
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
checkstyle
|
||||||
|
}
|
||||||
|
|
||||||
|
val jadxVersion: String by rootProject.extra
|
||||||
|
|
||||||
|
group = "io.github.skylot"
|
||||||
|
version = jadxVersion
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.slf4j:slf4j-api:2.0.11")
|
||||||
|
compileOnly("org.jetbrains:annotations:24.1.0")
|
||||||
|
|
||||||
|
testImplementation("ch.qos.logback:logback-classic:1.4.14")
|
||||||
|
testImplementation("org.hamcrest:hamcrest-library:2.2")
|
||||||
|
testImplementation("org.mockito:mockito-core:5.10.0")
|
||||||
|
testImplementation("org.assertj:assertj-core:3.25.2")
|
||||||
|
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
|
||||||
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
|
||||||
|
testCompileOnly("org.jetbrains:annotations:24.1.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
// required for: aapt-proto, r8, smali
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes("jadx-version" to jadxVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
maxParallelForks = Runtime.getRuntime().availableProcessors()
|
||||||
|
testLogging {
|
||||||
|
showExceptions = true
|
||||||
|
exceptionFormat = TestExceptionFormat.FULL
|
||||||
|
showCauses = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("jadx-java")
|
||||||
|
id("org.jetbrains.kotlin.jvm")
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget.set(JvmTarget.JVM_11)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
plugins {
|
||||||
|
id("jadx-java")
|
||||||
|
id("java-library")
|
||||||
|
id("maven-publish")
|
||||||
|
id("signing")
|
||||||
|
}
|
||||||
|
|
||||||
|
val jadxVersion: String by rootProject.extra
|
||||||
|
|
||||||
|
group = "io.github.skylot"
|
||||||
|
version = jadxVersion
|
||||||
|
|
||||||
|
java {
|
||||||
|
withJavadocJar()
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("mavenJava") {
|
||||||
|
artifactId = project.name
|
||||||
|
from(components["java"])
|
||||||
|
versionMapping {
|
||||||
|
usage("java-api") {
|
||||||
|
fromResolutionOf("runtimeClasspath")
|
||||||
|
}
|
||||||
|
usage("java-runtime") {
|
||||||
|
fromResolutionResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pom {
|
||||||
|
name.set(project.name)
|
||||||
|
description.set("Dex to Java decompiler")
|
||||||
|
url.set("https://github.com/skylot/jadx")
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name.set("The Apache License, Version 2.0")
|
||||||
|
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id.set("skylot")
|
||||||
|
name.set("Skylot")
|
||||||
|
email.set("skylot@gmail.com")
|
||||||
|
url.set("https://github.com/skylot")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection .set("scm:git:git://github.com/skylot/jadx.git")
|
||||||
|
developerConnection.set("scm:git:ssh://github.com:skylot/jadx.git")
|
||||||
|
url .set("https://github.com/skylot/jadx")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
val releasesRepoUrl = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
|
||||||
|
val snapshotsRepoUrl = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||||
|
url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
|
||||||
|
credentials {
|
||||||
|
username = project.properties["ossrhUser"].toString()
|
||||||
|
password = project.properties["ossrhPassword"].toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signing {
|
||||||
|
isRequired = gradle.taskGraph.hasTask("publish")
|
||||||
|
sign(publishing.publications["mavenJava"])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks.javadoc {
|
||||||
|
val stdOptions = options as StandardJavadocDocletOptions
|
||||||
|
stdOptions.addBooleanOption("html5", true)
|
||||||
|
// disable 'missing' warnings
|
||||||
|
stdOptions.addStringOption("Xdoclint:all,-missing", "-quiet")
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
|
||||||
|
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
|
||||||
|
|
||||||
|
<module name="Checker">
|
||||||
|
<property name="fileExtensions" value="java, properties, xml"/>
|
||||||
|
<property name="charset" value="UTF-8"/>
|
||||||
|
|
||||||
|
<module name="TreeWalker">
|
||||||
|
<property name="tabWidth" value="4"/>
|
||||||
|
<module name="RegexpSinglelineJava">
|
||||||
|
<property name="format" value="^\t* "/>
|
||||||
|
<property name="message" value="Indent must use tab characters"/>
|
||||||
|
<property name="ignoreComments" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="RegexpSinglelineJava">
|
||||||
|
<property name="format" value="^(?!\s+\* $).*?\s+$"/>
|
||||||
|
<property name="message" value="Line has trailing spaces."/>
|
||||||
|
</module>
|
||||||
|
<module name="AvoidEscapedUnicodeCharacters">
|
||||||
|
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||||
|
<property name="allowByTailComment" value="true"/>
|
||||||
|
<property name="allowNonPrintableEscapes" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="EmptyLineSeparator">
|
||||||
|
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||||
|
<property name="allowMultipleEmptyLines" value="false"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- whitespaces -->
|
||||||
|
<module name="SingleSpaceSeparator"/>
|
||||||
|
<module name="GenericWhitespace"/>
|
||||||
|
<module name="MethodParamPad"/>
|
||||||
|
<module name="NoWhitespaceBefore"/>
|
||||||
|
<module name="OperatorWrap"/>
|
||||||
|
<module name="ParenPad"/>
|
||||||
|
<module name="TypecastParenPad"/>
|
||||||
|
<module name="WhitespaceAfter"/>
|
||||||
|
<module name="WhitespaceAround">
|
||||||
|
<property name="allowEmptyMethods" value="true"/>
|
||||||
|
</module>
|
||||||
|
<!-- <module name="EmptyForIteratorPad"/> -->
|
||||||
|
<!-- <module name="NoWhitespaceAfter"/>-->
|
||||||
|
|
||||||
|
<module name="NoLineWrap"/>
|
||||||
|
|
||||||
|
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
|
||||||
|
<module name="RedundantImport"/>
|
||||||
|
<module name="UnusedImports"/>
|
||||||
|
<!-- <module name="AvoidStarImport"/> -->
|
||||||
|
|
||||||
|
<module name="NeedBraces"/>
|
||||||
|
<module name="LeftCurly"/>
|
||||||
|
<module name="RightCurly"/>
|
||||||
|
<module name="EmptyCatchBlock">
|
||||||
|
<property name="exceptionVariableName" value="expected|ignore"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- naming -->
|
||||||
|
<module name="PackageName"/>
|
||||||
|
<module name="TypeName"/>
|
||||||
|
<module name="InterfaceTypeParameterName"/>
|
||||||
|
<module name="ClassTypeParameterName"/>
|
||||||
|
<module name="StaticVariableName"/>
|
||||||
|
<module name="ConstantName"/>
|
||||||
|
<module name="MemberName"/>
|
||||||
|
<module name="MethodName"/>
|
||||||
|
<module name="MethodTypeParameterName"/>
|
||||||
|
<module name="ParameterName"/>
|
||||||
|
<module name="LambdaParameterName"/>
|
||||||
|
<module name="LocalVariableName"/>
|
||||||
|
<module name="LocalFinalVariableName"/>
|
||||||
|
<module name="CatchParameterName"/>
|
||||||
|
<!-- <module name="HiddenField"/> -->
|
||||||
|
|
||||||
|
<!-- annotations -->
|
||||||
|
<module name="AnnotationLocation"/>
|
||||||
|
<module name="AnnotationUseStyle">
|
||||||
|
<property name="elementStyle" value="compact"/>
|
||||||
|
</module>
|
||||||
|
<module name="MissingOverride"/>
|
||||||
|
<!-- <module name="MissingDeprecated"/> -->
|
||||||
|
|
||||||
|
<module name="ModifierOrder"/>
|
||||||
|
<!-- <module name="RedundantModifier"/> -->
|
||||||
|
<!-- <module name="ParameterNumber"/> -->
|
||||||
|
|
||||||
|
<module name="EmptyStatement"/>
|
||||||
|
<module name="DefaultComesLast"/>
|
||||||
|
<module name="EqualsHashCode"/>
|
||||||
|
<module name="FallThrough"/>
|
||||||
|
<!-- <module name="IllegalCatch"/> -->
|
||||||
|
<module name="IllegalThrows"/>
|
||||||
|
<module name="IllegalType"/>
|
||||||
|
<module name="InnerAssignment"/>
|
||||||
|
<module name="MultipleVariableDeclarations"/>
|
||||||
|
<module name="NoClone"/>
|
||||||
|
<module name="NoFinalizer"/>
|
||||||
|
<module name="OneStatementPerLine"/>
|
||||||
|
<module name="PackageDeclaration"/>
|
||||||
|
<module name="StringLiteralEquality"/>
|
||||||
|
|
||||||
|
<!-- design -->
|
||||||
|
<module name="OneTopLevelClass"/>
|
||||||
|
<module name="MutableException"/>
|
||||||
|
<module name="InterfaceIsType"/>
|
||||||
|
<module name="ThrowsCount">
|
||||||
|
<property name="max" value="2"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- misc -->
|
||||||
|
<module name="ArrayTypeStyle"/>
|
||||||
|
<module name="OuterTypeFilename"/>
|
||||||
|
|
||||||
|
<!-- sizes -->
|
||||||
|
<module name="OuterTypeNumber"/>
|
||||||
|
|
||||||
|
<module name="SuppressWarningsHolder"/>
|
||||||
|
|
||||||
|
<module name="IllegalType"/>
|
||||||
|
<module name="IllegalImport">
|
||||||
|
<property name="illegalClasses" value="jadx.core.utils.DebugUtils"/>
|
||||||
|
</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"/>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NewlineAtEndOfFile"/>
|
||||||
|
<module name="SuppressWarningsFilter"/>
|
||||||
|
</module>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#Import Order
|
||||||
|
0=java
|
||||||
|
1=javax
|
||||||
|
2=org
|
||||||
|
3=com
|
||||||
|
4=
|
||||||
|
5=jadx
|
||||||
|
6=\#
|
||||||
@@ -0,0 +1,400 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<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.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_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.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||||
|
<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.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||||
|
<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.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
|
||||||
|
<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.continuation_indentation" value="2"/>
|
||||||
|
<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.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.comment.count_line_length_from_starting_position" value="true"/>
|
||||||
|
<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_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.insert_space_before_closing_brace_in_array_initializer" value="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.alignment_for_parameterized_type_references" value="0"/>
|
||||||
|
<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.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"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_never"/>
|
||||||
|
<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.brace_position_for_method_declaration" value="end_of_line"/>
|
||||||
|
<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.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||||
|
<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_multiple_field_declarations" value="insert"/>
|
||||||
|
<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.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_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_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.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.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.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="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_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_after_string_concatenation" value="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.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.alignment_for_conditional_expression" value="48"/>
|
||||||
|
<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.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.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.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.keep_code_block_on_one_line" value="one_line_never"/>
|
||||||
|
<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_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.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.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.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.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_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||||
|
<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.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.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.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_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_opening_angle_bracket_in_parameterized_type_reference" 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_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_space_before_logical_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||||
|
<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_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.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="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.keep_simple_while_body_on_same_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
|
||||||
|
<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_before_colon_in_case" value="do not 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.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_comma_in_for_increments" 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.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||||
|
<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.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.wrap_before_string_concatenation" value="true"/>
|
||||||
|
<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"/>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
SmaliTokenMaker.java
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Refer to the following instructions to modify and use to generate SmaliTokenMarker
|
||||||
|
|
||||||
|
```shell
|
||||||
|
jflex SmaliTokenMaker.flex --skel skeleton.default
|
||||||
|
```
|
||||||
@@ -0,0 +1,681 @@
|
|||||||
|
/*
|
||||||
|
* Generated on 11/22/21, 8:58 PM
|
||||||
|
*/
|
||||||
|
package jadx.gui.ui.codeearea;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import javax.swing.text.Segment;
|
||||||
|
|
||||||
|
import org.fife.ui.rsyntaxtextarea.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于Smali代码高亮
|
||||||
|
* MartinKay@qq.com
|
||||||
|
*/
|
||||||
|
%%
|
||||||
|
|
||||||
|
%public
|
||||||
|
%class SmaliTokenMaker
|
||||||
|
%extends AbstractJFlexCTokenMaker
|
||||||
|
%unicode
|
||||||
|
/* Case sensitive */
|
||||||
|
%type org.fife.ui.rsyntaxtextarea.Token
|
||||||
|
|
||||||
|
|
||||||
|
%{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. This must be here because JFlex does not generate a
|
||||||
|
* no-parameter constructor.
|
||||||
|
*/
|
||||||
|
public SmaliTokenMaker() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the token specified to the current linked list of tokens.
|
||||||
|
*
|
||||||
|
* @param tokenType The token's type.
|
||||||
|
* @see #addToken(int, int, int)
|
||||||
|
*/
|
||||||
|
private void addHyperlinkToken(int start, int end, int tokenType) {
|
||||||
|
int so = start + offsetShift;
|
||||||
|
addToken(zzBuffer, start,end, tokenType, so, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the token specified to the current linked list of tokens.
|
||||||
|
*
|
||||||
|
* @param tokenType The token's type.
|
||||||
|
*/
|
||||||
|
private void addToken(int tokenType) {
|
||||||
|
addToken(zzStartRead, zzMarkedPos-1, tokenType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the token specified to the current linked list of tokens.
|
||||||
|
*
|
||||||
|
* @param tokenType The token's type.
|
||||||
|
* @see #addHyperlinkToken(int, int, int)
|
||||||
|
*/
|
||||||
|
private void addToken(int start, int end, int tokenType) {
|
||||||
|
int so = start + offsetShift;
|
||||||
|
addToken(zzBuffer, start,end, tokenType, so, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the token specified to the current linked list of tokens.
|
||||||
|
*
|
||||||
|
* @param array The character array.
|
||||||
|
* @param start The starting offset in the array.
|
||||||
|
* @param end The ending offset in the array.
|
||||||
|
* @param tokenType The token's type.
|
||||||
|
* @param startOffset The offset in the document at which this token
|
||||||
|
* occurs.
|
||||||
|
* @param hyperlink Whether this token is a hyperlink.
|
||||||
|
*/
|
||||||
|
public void addToken(char[] array, int start, int end, int tokenType,
|
||||||
|
int startOffset, boolean hyperlink) {
|
||||||
|
super.addToken(array, start,end, tokenType, startOffset, hyperlink);
|
||||||
|
zzStartRead = zzMarkedPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public String[] getLineCommentStartAndEnd(int languageIndex) {
|
||||||
|
return new String[] { "#", null };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first token in the linked list of tokens generated
|
||||||
|
* from <code>text</code>. This method must be implemented by
|
||||||
|
* subclasses so they can correctly implement syntax highlighting.
|
||||||
|
*
|
||||||
|
* @param text The text from which to get tokens.
|
||||||
|
* @param initialTokenType The token type we should start with.
|
||||||
|
* @param startOffset The offset into the document at which
|
||||||
|
* <code>text</code> starts.
|
||||||
|
* @return The first <code>Token</code> in a linked list representing
|
||||||
|
* the syntax highlighted text.
|
||||||
|
*/
|
||||||
|
public Token getTokenList(Segment text, int initialTokenType, int startOffset) {
|
||||||
|
|
||||||
|
resetTokenList();
|
||||||
|
this.offsetShift = -text.offset + startOffset;
|
||||||
|
|
||||||
|
// Start off in the proper state.
|
||||||
|
int state = Token.NULL;
|
||||||
|
switch (initialTokenType) {
|
||||||
|
/* No multi-line comments */
|
||||||
|
/* No documentation comments */
|
||||||
|
default:
|
||||||
|
state = Token.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = text;
|
||||||
|
try {
|
||||||
|
yyreset(zzReader);
|
||||||
|
yybegin(state);
|
||||||
|
return yylex();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
return new TokenImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refills the input buffer.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if EOF was reached, otherwise
|
||||||
|
* <code>false</code>.
|
||||||
|
*/
|
||||||
|
private boolean zzRefill() {
|
||||||
|
return zzCurrentPos>=s.offset+s.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the scanner to read from a new input stream.
|
||||||
|
* Does not close the old reader.
|
||||||
|
*
|
||||||
|
* All internal variables are reset, the old input stream
|
||||||
|
* <b>cannot</b> be reused (internal buffer is discarded and lost).
|
||||||
|
* Lexical state is set to <tt>YY_INITIAL</tt>.
|
||||||
|
*
|
||||||
|
* @param reader the new input stream
|
||||||
|
*/
|
||||||
|
public final void yyreset(Reader reader) {
|
||||||
|
// 's' has been updated.
|
||||||
|
zzBuffer = s.array;
|
||||||
|
/*
|
||||||
|
* We replaced the line below with the two below it because zzRefill
|
||||||
|
* no longer "refills" the buffer (since the way we do it, it's always
|
||||||
|
* "full" the first time through, since it points to the segment's
|
||||||
|
* array). So, we assign zzEndRead here.
|
||||||
|
*/
|
||||||
|
//zzStartRead = zzEndRead = s.offset;
|
||||||
|
zzStartRead = s.offset;
|
||||||
|
zzEndRead = zzStartRead + s.count - 1;
|
||||||
|
zzCurrentPos = zzMarkedPos = zzPushbackPos = s.offset;
|
||||||
|
zzLexicalState = YYINITIAL;
|
||||||
|
zzReader = reader;
|
||||||
|
zzAtBOL = true;
|
||||||
|
zzAtEOF = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
Letter = [A-Za-z]
|
||||||
|
LetterOrUnderscore = ({Letter}|"_")
|
||||||
|
NonzeroDigit = [1-9]
|
||||||
|
Digit = ("0"|{NonzeroDigit})
|
||||||
|
HexDigit = ({Digit}|[A-Fa-f])
|
||||||
|
OctalDigit = ([0-7])
|
||||||
|
AnyCharacterButApostropheOrBackSlash = ([^\\'])
|
||||||
|
AnyCharacterButDoubleQuoteOrBackSlash = ([^\\\"\n])
|
||||||
|
EscapedSourceCharacter = ("u"{HexDigit}{HexDigit}{HexDigit}{HexDigit})
|
||||||
|
Escape = ("\\"(([btnfr\"'\\])|([0123]{OctalDigit}?{OctalDigit}?)|({OctalDigit}{OctalDigit}?)|{EscapedSourceCharacter}))
|
||||||
|
NonSeparator = ([^\t\f\r\n\ \(\)\{\}\[\]\;\,\.\=\>\<\!\~\?\:\+\-\*\/\&\|\^\%\"\']|"#"|"\\")
|
||||||
|
IdentifierStart = ({LetterOrUnderscore}|"$")
|
||||||
|
IdentifierPart = ({IdentifierStart}|{Digit}|("\\"{EscapedSourceCharacter}))
|
||||||
|
|
||||||
|
LineTerminator = (\n)
|
||||||
|
WhiteSpace = ([ \t\f]+)
|
||||||
|
|
||||||
|
CharLiteral = ([\']({AnyCharacterButApostropheOrBackSlash}|{Escape})[\'])
|
||||||
|
UnclosedCharLiteral = ([\'][^\'\n]*)
|
||||||
|
ErrorCharLiteral = ({UnclosedCharLiteral}[\'])
|
||||||
|
StringLiteral = ([\"]({AnyCharacterButDoubleQuoteOrBackSlash}|{Escape})*[\"])
|
||||||
|
UnclosedStringLiteral = ([\"]([\\].|[^\\\"])*[^\"]?)
|
||||||
|
ErrorStringLiteral = ({UnclosedStringLiteral}[\"])
|
||||||
|
|
||||||
|
/* No multi-line comments */
|
||||||
|
/* No documentation comments */
|
||||||
|
LineCommentBegin = "#"
|
||||||
|
|
||||||
|
IntegerLiteral = ({Digit}+)
|
||||||
|
HexLiteral = (0x{HexDigit}+)
|
||||||
|
FloatLiteral = (({Digit}+)("."{Digit}+)?(e[+-]?{Digit}+)? | ({Digit}+)?("."{Digit}+)(e[+-]?{Digit}+)?)
|
||||||
|
ErrorNumberFormat = (({IntegerLiteral}|{HexLiteral}|{FloatLiteral}){NonSeparator}+)
|
||||||
|
BooleanLiteral = ("true"|"false")
|
||||||
|
|
||||||
|
Separator = ([\(\)\{\}\[\]])
|
||||||
|
Separator2 = ([\;,.])
|
||||||
|
|
||||||
|
Identifier = ({IdentifierStart}{IdentifierPart}*)
|
||||||
|
|
||||||
|
URLGenDelim = ([:\/\?#\[\]@])
|
||||||
|
URLSubDelim = ([\!\$&'\(\)\*\+,;=])
|
||||||
|
URLUnreserved = ({LetterOrUnderscore}|{Digit}|[\-\.\~])
|
||||||
|
URLCharacter = ({URLGenDelim}|{URLSubDelim}|{URLUnreserved}|[%])
|
||||||
|
URLCharacters = ({URLCharacter}*)
|
||||||
|
URLEndCharacter = ([\/\$]|{Letter}|{Digit})
|
||||||
|
URL = (((https?|f(tp|ile))"://"|"www.")({URLCharacters}{URLEndCharacter})?)
|
||||||
|
|
||||||
|
|
||||||
|
/* Custom Regex */
|
||||||
|
/* fully-qualified name Rules */
|
||||||
|
SimpleName = ([a-zA-Z0-9_$]*)
|
||||||
|
QUALIFIED_TYPE_NAME = ("L"({SimpleName}{SLASH})*{SimpleName}*";")
|
||||||
|
/* Types */
|
||||||
|
VOID_TYPE = ("V")
|
||||||
|
BOOLEAN_TYPE = ("Z")
|
||||||
|
BYTE_TYPE = ("B")
|
||||||
|
SHORT_TYPE = ("S")
|
||||||
|
CHAR_TYPE = ("C")
|
||||||
|
INT_TYPE = ("I")
|
||||||
|
LONG_TYPE = ("J")
|
||||||
|
FLOAT_TYPE = ("F")
|
||||||
|
DOUBLE_TYPE = ("D")
|
||||||
|
/* Multi Args Types Highlight */
|
||||||
|
MULTI_ARGS_TYPES = (({BOOLEAN_TYPE}|{BYTE_TYPE}|{SHORT_TYPE}|{CHAR_TYPE}|{INT_TYPE}|{LONG_TYPE}|{FLOAT_TYPE}|{DOUBLE_TYPE})+);
|
||||||
|
|
||||||
|
/* Types fully-qualified name */
|
||||||
|
COMPOUND_METHOD_ARG_LITERAL = (({BOOLEAN_TYPE}|{BYTE_TYPE}|{SHORT_TYPE}|{CHAR_TYPE}|{INT_TYPE}|{LONG_TYPE}|{FLOAT_TYPE}|{DOUBLE_TYPE})+{QUALIFIED_TYPE_NAME})
|
||||||
|
|
||||||
|
|
||||||
|
LBRACK = ("[")
|
||||||
|
RBRACK = ("]")
|
||||||
|
LPAREN = ("(")
|
||||||
|
RPAREN = (")")
|
||||||
|
LBRACE = ("{")
|
||||||
|
RBRACE = ("}")
|
||||||
|
COLON = (":")
|
||||||
|
ASSIGN = ("=")
|
||||||
|
DOT = (".")
|
||||||
|
SUB = ("-")
|
||||||
|
COMMA = (",")
|
||||||
|
SLASH = ("/")
|
||||||
|
LT = ("<")
|
||||||
|
GT = (">")
|
||||||
|
ARROW = ("->")
|
||||||
|
SEMI = (";")
|
||||||
|
ARROW_FUNCTION = ({ARROW}[a-zA-Z_$<>]*)
|
||||||
|
CustomSeparator = ({Separator}|{ARROW_FUNCTION})
|
||||||
|
|
||||||
|
/* Register */
|
||||||
|
VREGISTER = ("v"("0"|[1-9])*)
|
||||||
|
PREGISTER = ("p"("0"|[1-9])*)
|
||||||
|
|
||||||
|
/* Flags */
|
||||||
|
FLAG_PSWITCH = (":pswitch_"{SimpleName})
|
||||||
|
FLAG_PSWITCH_DATA = (":pswitch_data_"{SimpleName})
|
||||||
|
FLAG_GOTO = (":goto_"{SimpleName})
|
||||||
|
FLAG_COND = (":cond_"{SimpleName})
|
||||||
|
FLAG_TRY_START = (":try_start_"{SimpleName})
|
||||||
|
FLAG_TRY_END = (":try_end_"{SimpleName})
|
||||||
|
FLAG_CATCH = (":catch_"{SimpleName})
|
||||||
|
FLAG_CATCHALL = (":catchall_"{SimpleName})
|
||||||
|
FLAG_ARRAY = (":array_"{SimpleName})
|
||||||
|
|
||||||
|
/* No string state */
|
||||||
|
/* No char state */
|
||||||
|
/* No MLC state */
|
||||||
|
/* No documentation comment state */
|
||||||
|
%state EOL_COMMENT
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
<YYINITIAL> {
|
||||||
|
|
||||||
|
/* Keywords Instructions Highlight */
|
||||||
|
"nop" |
|
||||||
|
"move" |
|
||||||
|
"move/from16" |
|
||||||
|
"move/16" |
|
||||||
|
"move-wide" |
|
||||||
|
"move-wide/from16" |
|
||||||
|
"move-wide/16" |
|
||||||
|
"move-object" |
|
||||||
|
"move-object/from16" |
|
||||||
|
"move-object/16" |
|
||||||
|
"move-result" |
|
||||||
|
"move-result-wide" |
|
||||||
|
"move-result-object" |
|
||||||
|
"move-exception" |
|
||||||
|
"return-void" |
|
||||||
|
"return" |
|
||||||
|
"return-wide" |
|
||||||
|
"return-object" |
|
||||||
|
"const/4" |
|
||||||
|
"const/16" |
|
||||||
|
"const" |
|
||||||
|
"const/high16" |
|
||||||
|
"const-wide/16" |
|
||||||
|
"const-wide/32" |
|
||||||
|
"const-wide" |
|
||||||
|
"const-wide/high16" |
|
||||||
|
"const-string" |
|
||||||
|
"const-string/jumbo" |
|
||||||
|
"const-class" |
|
||||||
|
"monitor-enter" |
|
||||||
|
"monitor-exit" |
|
||||||
|
"check-cast" |
|
||||||
|
"instance-of" |
|
||||||
|
"array-length" |
|
||||||
|
"new-instance" |
|
||||||
|
"new-array" |
|
||||||
|
"filled-new-array" |
|
||||||
|
"filled-new-array/range" |
|
||||||
|
"fill-array-data" |
|
||||||
|
"throw" |
|
||||||
|
"goto" |
|
||||||
|
"goto/16" |
|
||||||
|
"goto/32" |
|
||||||
|
"cmpl-float" |
|
||||||
|
"cmpg-float" |
|
||||||
|
"cmpl-double" |
|
||||||
|
"cmpg-double" |
|
||||||
|
"cmp-long" |
|
||||||
|
"if-eq" |
|
||||||
|
"if-ne" |
|
||||||
|
"if-lt" |
|
||||||
|
"if-ge" |
|
||||||
|
"if-gt" |
|
||||||
|
"if-le" |
|
||||||
|
"if-eqz" |
|
||||||
|
"if-nez" |
|
||||||
|
"if-ltz" |
|
||||||
|
"if-gez" |
|
||||||
|
"if-gtz" |
|
||||||
|
"if-lez" |
|
||||||
|
"aget" |
|
||||||
|
"aget-wide" |
|
||||||
|
"aget-object" |
|
||||||
|
"aget-boolean" |
|
||||||
|
"aget-byte" |
|
||||||
|
"aget-char" |
|
||||||
|
"aget-short" |
|
||||||
|
"aput" |
|
||||||
|
"aput-wide" |
|
||||||
|
"aput-object" |
|
||||||
|
"aput-boolean" |
|
||||||
|
"aput-byte" |
|
||||||
|
"aput-char" |
|
||||||
|
"aput-short" |
|
||||||
|
"iget" |
|
||||||
|
"iget-wide" |
|
||||||
|
"iget-object" |
|
||||||
|
"iget-boolean" |
|
||||||
|
"iget-byte" |
|
||||||
|
"iget-char" |
|
||||||
|
"iget-short" |
|
||||||
|
"iput" |
|
||||||
|
"iput-wide" |
|
||||||
|
"iput-object" |
|
||||||
|
"iput-boolean" |
|
||||||
|
"iput-byte" |
|
||||||
|
"iput-char" |
|
||||||
|
"iput-short" |
|
||||||
|
"sget" |
|
||||||
|
"sget-wide" |
|
||||||
|
"sget-object" |
|
||||||
|
"sget-boolean" |
|
||||||
|
"sget-byte" |
|
||||||
|
"sget-char" |
|
||||||
|
"sget-short" |
|
||||||
|
"sput" |
|
||||||
|
"sput-wide" |
|
||||||
|
"sput-object" |
|
||||||
|
"sput-boolean" |
|
||||||
|
"sput-byte" |
|
||||||
|
"sput-char" |
|
||||||
|
"sput-short" |
|
||||||
|
"invoke-virtual" |
|
||||||
|
"invoke-super" |
|
||||||
|
"invoke-direct" |
|
||||||
|
"invoke-static" |
|
||||||
|
"invoke-interface" |
|
||||||
|
"invoke-virtual/range" |
|
||||||
|
"invoke-super/range" |
|
||||||
|
"invoke-direct/range" |
|
||||||
|
"invoke-static/range" |
|
||||||
|
"invoke-interface/range" |
|
||||||
|
"neg-int" |
|
||||||
|
"not-int" |
|
||||||
|
"neg-long" |
|
||||||
|
"not-long" |
|
||||||
|
"neg-float" |
|
||||||
|
"neg-double" |
|
||||||
|
"int-to-long" |
|
||||||
|
"int-to-float" |
|
||||||
|
"int-to-double" |
|
||||||
|
"long-to-int" |
|
||||||
|
"long-to-float" |
|
||||||
|
"long-to-double" |
|
||||||
|
"float-to-int" |
|
||||||
|
"float-to-long" |
|
||||||
|
"float-to-double" |
|
||||||
|
"double-to-int" |
|
||||||
|
"double-to-long" |
|
||||||
|
"double-to-float" |
|
||||||
|
"int-to-byte" |
|
||||||
|
"int-to-char" |
|
||||||
|
"int-to-short" |
|
||||||
|
"add-int" |
|
||||||
|
"sub-int" |
|
||||||
|
"mul-int" |
|
||||||
|
"div-int" |
|
||||||
|
"rem-int" |
|
||||||
|
"and-int" |
|
||||||
|
"or-int" |
|
||||||
|
"xor-int" |
|
||||||
|
"shl-int" |
|
||||||
|
"shr-int" |
|
||||||
|
"ushr-int" |
|
||||||
|
"add-long" |
|
||||||
|
"sub-long" |
|
||||||
|
"mul-long" |
|
||||||
|
"div-long" |
|
||||||
|
"rem-long" |
|
||||||
|
"and-long" |
|
||||||
|
"or-long" |
|
||||||
|
"xor-long" |
|
||||||
|
"shl-long" |
|
||||||
|
"shr-long" |
|
||||||
|
"ushr-long" |
|
||||||
|
"add-float" |
|
||||||
|
"sub-float" |
|
||||||
|
"mul-float" |
|
||||||
|
"div-float" |
|
||||||
|
"rem-float" |
|
||||||
|
"add-double" |
|
||||||
|
"sub-double" |
|
||||||
|
"mul-double" |
|
||||||
|
"div-double" |
|
||||||
|
"rem-double" |
|
||||||
|
"add-int/2addr" |
|
||||||
|
"sub-int/2addr" |
|
||||||
|
"mul-int/2addr" |
|
||||||
|
"div-int/2addr" |
|
||||||
|
"rem-int/2addr" |
|
||||||
|
"and-int/2addr" |
|
||||||
|
"or-int/2addr" |
|
||||||
|
"xor-int/2addr" |
|
||||||
|
"shl-int/2addr" |
|
||||||
|
"shr-int/2addr" |
|
||||||
|
"ushr-int/2addr" |
|
||||||
|
"add-long/2addr" |
|
||||||
|
"sub-long/2addr" |
|
||||||
|
"mul-long/2addr" |
|
||||||
|
"div-long/2addr" |
|
||||||
|
"rem-long/2addr" |
|
||||||
|
"and-long/2addr" |
|
||||||
|
"or-long/2addr" |
|
||||||
|
"xor-long/2addr" |
|
||||||
|
"shl-long/2addr" |
|
||||||
|
"shr-long/2addr" |
|
||||||
|
"ushr-long/2addr" |
|
||||||
|
"add-float/2addr" |
|
||||||
|
"sub-float/2addr" |
|
||||||
|
"mul-float/2addr" |
|
||||||
|
"div-float/2addr" |
|
||||||
|
"rem-float/2addr" |
|
||||||
|
"add-double/2addr" |
|
||||||
|
"sub-double/2addr" |
|
||||||
|
"mul-double/2addr" |
|
||||||
|
"div-double/2addr" |
|
||||||
|
"rem-double/2addr" |
|
||||||
|
"add-int/lit16" |
|
||||||
|
"rsub-int" |
|
||||||
|
"mul-int/lit16" |
|
||||||
|
"div-int/lit16" |
|
||||||
|
"rem-int/lit16" |
|
||||||
|
"and-int/lit16" |
|
||||||
|
"or-int/lit16" |
|
||||||
|
"xor-int/lit16" |
|
||||||
|
"add-int/lit8" |
|
||||||
|
"rsub-int/lit8" |
|
||||||
|
"mul-int/lit8" |
|
||||||
|
"div-int/lit8" |
|
||||||
|
"rem-int/lit8" |
|
||||||
|
"and-int/lit8" |
|
||||||
|
"or-int/lit8" |
|
||||||
|
"xor-int/lit8" |
|
||||||
|
"shl-int/lit8" |
|
||||||
|
"shr-int/lit8" |
|
||||||
|
"ushr-int/lit8" |
|
||||||
|
"invoke-polymorphic" |
|
||||||
|
"invoke-polymorphic/range" |
|
||||||
|
"invoke-custom" |
|
||||||
|
"invoke-custom/range" |
|
||||||
|
"const-method-handle" |
|
||||||
|
"const-method-type" |
|
||||||
|
"packed-switch" |
|
||||||
|
"sparse-switch" { addToken(Token.FUNCTION); }
|
||||||
|
|
||||||
|
/* Keywords Modifiers(IDENTIFIER标识符、修饰符) Highlight */
|
||||||
|
"public" |
|
||||||
|
"private" |
|
||||||
|
"protected" |
|
||||||
|
"final" |
|
||||||
|
"annotation" |
|
||||||
|
"static" |
|
||||||
|
"synthetic" |
|
||||||
|
"constructor" |
|
||||||
|
"abstract" |
|
||||||
|
"enum" |
|
||||||
|
"interface" |
|
||||||
|
"transient" |
|
||||||
|
"bridge" |
|
||||||
|
"declared-synchronized" |
|
||||||
|
"volatile" |
|
||||||
|
"strictfp" |
|
||||||
|
"varargs" |
|
||||||
|
"native" { addToken(Token.RESERVED_WORD); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Keywords Directives Highlight */
|
||||||
|
".method" |
|
||||||
|
".end method" |
|
||||||
|
".implements" |
|
||||||
|
".class" |
|
||||||
|
".prologue" |
|
||||||
|
".source" |
|
||||||
|
".super" |
|
||||||
|
".field" |
|
||||||
|
".end field" |
|
||||||
|
".registers" |
|
||||||
|
".locals" |
|
||||||
|
".param" |
|
||||||
|
".line" |
|
||||||
|
".catch" |
|
||||||
|
".catchall" |
|
||||||
|
".annotation" |
|
||||||
|
".end annotation" |
|
||||||
|
".local" |
|
||||||
|
".end local" |
|
||||||
|
".restart local" |
|
||||||
|
".packed-switch" |
|
||||||
|
".end packed-switch" |
|
||||||
|
".array-data" |
|
||||||
|
".end array-data" |
|
||||||
|
".sparse-switch" |
|
||||||
|
".end sparse-switch" |
|
||||||
|
".end param" { addToken(Token.RESERVED_WORD_2); }
|
||||||
|
|
||||||
|
|
||||||
|
/* VARIABLE Register Highlight */
|
||||||
|
{VREGISTER} |
|
||||||
|
{PREGISTER} { addToken(Token.VARIABLE); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Data types Highlight */
|
||||||
|
{QUALIFIED_TYPE_NAME} |
|
||||||
|
{COMPOUND_METHOD_ARG_LITERAL} |
|
||||||
|
{MULTI_ARGS_TYPES} |
|
||||||
|
{QUALIFIED_TYPE_NAME} |
|
||||||
|
{VOID_TYPE} |
|
||||||
|
{BOOLEAN_TYPE} |
|
||||||
|
{BYTE_TYPE} |
|
||||||
|
{SHORT_TYPE} |
|
||||||
|
{CHAR_TYPE} |
|
||||||
|
{INT_TYPE} |
|
||||||
|
{LONG_TYPE} |
|
||||||
|
{FLOAT_TYPE} |
|
||||||
|
{DOUBLE_TYPE} { addToken(Token.DATA_TYPE); }
|
||||||
|
|
||||||
|
/* FLAGS */
|
||||||
|
{FLAG_PSWITCH} |
|
||||||
|
{FLAG_PSWITCH_DATA} |
|
||||||
|
{FLAG_GOTO} |
|
||||||
|
{FLAG_COND} |
|
||||||
|
{FLAG_TRY_START} |
|
||||||
|
{FLAG_TRY_END} |
|
||||||
|
{FLAG_CATCHALL} |
|
||||||
|
{FLAG_ARRAY} |
|
||||||
|
{FLAG_CATCH} { addToken(Token.MARKUP_TAG_NAME); }
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
/* No functions */
|
||||||
|
|
||||||
|
{BooleanLiteral} { addToken(Token.LITERAL_BOOLEAN); }
|
||||||
|
|
||||||
|
{LineTerminator} { addNullToken(); return firstToken; }
|
||||||
|
|
||||||
|
{Identifier} { addToken(Token.IDENTIFIER); }
|
||||||
|
|
||||||
|
{WhiteSpace} { addToken(Token.WHITESPACE); }
|
||||||
|
|
||||||
|
/* String/Character literals. */
|
||||||
|
{CharLiteral} { addToken(Token.LITERAL_CHAR); }
|
||||||
|
{UnclosedCharLiteral} { addToken(Token.ERROR_CHAR); addNullToken(); return firstToken; }
|
||||||
|
{ErrorCharLiteral} { addToken(Token.ERROR_CHAR); }
|
||||||
|
{StringLiteral} { addToken(Token.LITERAL_STRING_DOUBLE_QUOTE); }
|
||||||
|
{UnclosedStringLiteral} { addToken(Token.ERROR_STRING_DOUBLE); addNullToken(); return firstToken; }
|
||||||
|
{ErrorStringLiteral} { addToken(Token.ERROR_STRING_DOUBLE); }
|
||||||
|
|
||||||
|
/* Comment literals. */
|
||||||
|
/* No multi-line comments */
|
||||||
|
/* No documentation comments */
|
||||||
|
{LineCommentBegin} { start = zzMarkedPos-1; yybegin(EOL_COMMENT); }
|
||||||
|
|
||||||
|
/* Separators. */
|
||||||
|
{CustomSeparator} { addToken(Token.SEPARATOR); }
|
||||||
|
{Separator2} { addToken(Token.IDENTIFIER); }
|
||||||
|
|
||||||
|
/* Operators. */
|
||||||
|
"!" |
|
||||||
|
";" |
|
||||||
|
"." |
|
||||||
|
"=" |
|
||||||
|
"/" |
|
||||||
|
"'" |
|
||||||
|
"(" |
|
||||||
|
")" |
|
||||||
|
"," |
|
||||||
|
"->" |
|
||||||
|
";->" |
|
||||||
|
"<" |
|
||||||
|
">" |
|
||||||
|
"@" |
|
||||||
|
"[" |
|
||||||
|
"]" |
|
||||||
|
"{" |
|
||||||
|
"}" { addToken(Token.OPERATOR); }
|
||||||
|
|
||||||
|
/* Numbers */
|
||||||
|
{IntegerLiteral} { addToken(Token.LITERAL_NUMBER_DECIMAL_INT); }
|
||||||
|
{HexLiteral} { addToken(Token.LITERAL_NUMBER_HEXADECIMAL); }
|
||||||
|
{FloatLiteral} { addToken(Token.LITERAL_NUMBER_FLOAT); }
|
||||||
|
{ErrorNumberFormat} { addToken(Token.ERROR_NUMBER_FORMAT); }
|
||||||
|
|
||||||
|
/* Ended with a line not in a string or comment. */
|
||||||
|
<<EOF>> { addNullToken(); return firstToken; }
|
||||||
|
|
||||||
|
/* Catch any other (unhandled) characters. */
|
||||||
|
. { addToken(Token.IDENTIFIER); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* No char state */
|
||||||
|
|
||||||
|
/* No string state */
|
||||||
|
|
||||||
|
/* No multi-line comment state */
|
||||||
|
|
||||||
|
/* No documentation comment state */
|
||||||
|
|
||||||
|
<EOL_COMMENT> {
|
||||||
|
[^hwf\n]+ {}
|
||||||
|
{URL} { int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_EOL); addHyperlinkToken(temp,zzMarkedPos-1, Token.COMMENT_EOL); start = zzMarkedPos; }
|
||||||
|
[hwf] {}
|
||||||
|
\n { addToken(start,zzStartRead-1, Token.COMMENT_EOL); addNullToken(); return firstToken; }
|
||||||
|
<<EOF>> { addToken(start,zzStartRead-1, Token.COMMENT_EOL); addNullToken(); return firstToken; }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
|
||||||
|
/** This character denotes the end of file */
|
||||||
|
public static final int YYEOF = -1;
|
||||||
|
|
||||||
|
/** initial size of the lookahead buffer */
|
||||||
|
--- private static final int ZZ_BUFFERSIZE = ...;
|
||||||
|
|
||||||
|
/** lexical states */
|
||||||
|
--- lexical states, charmap
|
||||||
|
|
||||||
|
/* error codes */
|
||||||
|
private static final int ZZ_UNKNOWN_ERROR = 0;
|
||||||
|
private static final int ZZ_NO_MATCH = 1;
|
||||||
|
private static final int ZZ_PUSHBACK_2BIG = 2;
|
||||||
|
|
||||||
|
/* error messages for the codes above */
|
||||||
|
private static final String ZZ_ERROR_MSG[] = {
|
||||||
|
"Unkown internal scanner error",
|
||||||
|
"Error: could not match input",
|
||||||
|
"Error: pushback value was too large"
|
||||||
|
};
|
||||||
|
|
||||||
|
--- isFinal list
|
||||||
|
/** the input device */
|
||||||
|
private java.io.Reader zzReader;
|
||||||
|
|
||||||
|
/** the current state of the DFA */
|
||||||
|
private int zzState;
|
||||||
|
|
||||||
|
/** the current lexical state */
|
||||||
|
private int zzLexicalState = YYINITIAL;
|
||||||
|
|
||||||
|
/** this buffer contains the current text to be matched and is
|
||||||
|
the source of the yytext() string */
|
||||||
|
private char zzBuffer[];
|
||||||
|
|
||||||
|
/** the textposition at the last accepting state */
|
||||||
|
private int zzMarkedPos;
|
||||||
|
|
||||||
|
/** the textposition at the last state to be included in yytext */
|
||||||
|
private int zzPushbackPos;
|
||||||
|
|
||||||
|
/** the current text position in the buffer */
|
||||||
|
private int zzCurrentPos;
|
||||||
|
|
||||||
|
/** startRead marks the beginning of the yytext() string in the buffer */
|
||||||
|
private int zzStartRead;
|
||||||
|
|
||||||
|
/** endRead marks the last character in the buffer, that has been read
|
||||||
|
from input */
|
||||||
|
private int zzEndRead;
|
||||||
|
|
||||||
|
/** number of newlines encountered up to the start of the matched text */
|
||||||
|
private int yyline;
|
||||||
|
|
||||||
|
/** the number of characters up to the start of the matched text */
|
||||||
|
private int yychar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/** zzAtEOF == true <=> the scanner is at the EOF */
|
||||||
|
private boolean zzAtEOF;
|
||||||
|
|
||||||
|
--- user class code
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new scanner
|
||||||
|
* There is also a java.io.InputStream version of this constructor.
|
||||||
|
*
|
||||||
|
* @param in the java.io.Reader to read input from.
|
||||||
|
*/
|
||||||
|
--- constructor declaration
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the input stream.
|
||||||
|
*/
|
||||||
|
public final void yyclose() throws java.io.IOException {
|
||||||
|
zzAtEOF = true; /* indicate end of file */
|
||||||
|
zzEndRead = zzStartRead; /* invalidate buffer */
|
||||||
|
|
||||||
|
if (zzReader != null)
|
||||||
|
zzReader.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enters a new lexical state
|
||||||
|
*
|
||||||
|
* @param newState the new lexical state
|
||||||
|
*/
|
||||||
|
public final void yybegin(int newState) {
|
||||||
|
zzLexicalState = newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text matched by the current regular expression.
|
||||||
|
*/
|
||||||
|
public final String yytext() {
|
||||||
|
return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* A value from 0 to yylength()-1.
|
||||||
|
*
|
||||||
|
* @return the character at position pos
|
||||||
|
*/
|
||||||
|
public final char yycharat(int pos) {
|
||||||
|
return zzBuffer[zzStartRead+pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of the matched text region.
|
||||||
|
*/
|
||||||
|
public final int yylength() {
|
||||||
|
return zzMarkedPos-zzStartRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* 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.).
|
||||||
|
*
|
||||||
|
* Usual syntax/scanner level error handling should be done
|
||||||
|
* in error fallback rules.
|
||||||
|
*
|
||||||
|
* @param errorCode the code of the errormessage to display
|
||||||
|
*/
|
||||||
|
--- zzScanError declaration
|
||||||
|
String message;
|
||||||
|
try {
|
||||||
|
message = ZZ_ERROR_MSG[errorCode];
|
||||||
|
}
|
||||||
|
catch (ArrayIndexOutOfBoundsException e) {
|
||||||
|
message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
|
||||||
|
}
|
||||||
|
|
||||||
|
--- throws clause
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes the specified amount of characters back into the input stream.
|
||||||
|
*
|
||||||
|
* They will be read again by then next call of the scanning method
|
||||||
|
*
|
||||||
|
* @param number the number of characters to be read again.
|
||||||
|
* This number must not be greater than yylength()!
|
||||||
|
*/
|
||||||
|
--- yypushback decl (contains zzScanError exception)
|
||||||
|
if ( number > yylength() )
|
||||||
|
zzScanError(ZZ_PUSHBACK_2BIG);
|
||||||
|
|
||||||
|
zzMarkedPos -= number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--- zzDoEOF
|
||||||
|
/**
|
||||||
|
* Resumes scanning until the next regular expression is matched,
|
||||||
|
* the end of input is encountered or an I/O-Error occurs.
|
||||||
|
*
|
||||||
|
* @return the next token
|
||||||
|
* @exception java.io.IOException if any I/O-Error occurs
|
||||||
|
*/
|
||||||
|
--- yylex declaration
|
||||||
|
int zzInput;
|
||||||
|
int zzAction;
|
||||||
|
|
||||||
|
// cached fields:
|
||||||
|
int zzCurrentPosL;
|
||||||
|
int zzMarkedPosL;
|
||||||
|
int zzEndReadL = zzEndRead;
|
||||||
|
char [] zzBufferL = zzBuffer;
|
||||||
|
char [] zzCMapL = ZZ_CMAP;
|
||||||
|
|
||||||
|
--- local declarations
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
zzMarkedPosL = zzMarkedPos;
|
||||||
|
|
||||||
|
--- start admin (line, char, col count)
|
||||||
|
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;
|
||||||
|
--- line count update
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// store back cached position
|
||||||
|
zzMarkedPos = zzMarkedPosL;
|
||||||
|
--- char count update
|
||||||
|
|
||||||
|
--- actions
|
||||||
|
default:
|
||||||
|
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
|
||||||
|
zzAtEOF = true;
|
||||||
|
--- eofvalue
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
--- no match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--- main
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
org.gradle.warning.mode=all
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.caching=true
|
||||||
|
|
||||||
|
# Flags for google-java-format (optimize imports by spotless) for Java >= 16.
|
||||||
|
# Java < 9 will ignore unsupported flags (thanks to -XX:+IgnoreUnrecognizedVMOptions)
|
||||||
|
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
-1
@@ -1,5 +1,8 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.9-bin.zip
|
|
||||||
|
|||||||
@@ -1,79 +1,127 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 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.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Attempt to set APP_HOME
|
||||||
DEFAULT_JVM_OPTS=""
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
# Resolve links: $0 may be a link
|
||||||
APP_BASE_NAME=`basename "$0"`
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn ( ) {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die ( ) {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
case "`uname`" in
|
nonstop=false
|
||||||
CYGWIN* )
|
case "$( uname )" in #(
|
||||||
cygwin=true
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
;;
|
Darwin* ) darwin=true ;; #(
|
||||||
Darwin* )
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
darwin=true
|
NONSTOP* ) nonstop=true ;;
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
|
||||||
if $cygwin ; then
|
|
||||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
|
||||||
# Resolve links: $0 may be a link
|
|
||||||
PRG="$0"
|
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
|
||||||
ls=`ls -ld "$PRG"`
|
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
|
||||||
PRG="$link"
|
|
||||||
else
|
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >&-
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >&-
|
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -82,83 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
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
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
ulimit -n $MAX_FD
|
warn "Could not query maximum file descriptor limit"
|
||||||
if [ $? -ne 0 ] ; then
|
esac
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
case $MAX_FD in #(
|
||||||
fi
|
'' | soft) :;; #(
|
||||||
else
|
*)
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
fi
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
|
||||||
if $cygwin ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=$((i+1))
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
(0) set -- ;;
|
|
||||||
(1) set -- "$args0" ;;
|
|
||||||
(2) set -- "$args0" "$args1" ;;
|
|
||||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
function splitJvmOpts() {
|
# * args from the command line
|
||||||
JVM_OPTS=("$@")
|
# * the main class name
|
||||||
}
|
# * -classpath
|
||||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
# * -D...appname settings
|
||||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
# 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" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
Vendored
+92
-90
@@ -1,90 +1,92 @@
|
|||||||
@if "%DEBUG%" == "" @echo off
|
@rem
|
||||||
@rem ##########################################################################
|
@rem Copyright 2015 the original author or authors.
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@rem
|
@rem you may not use this file except in compliance with the License.
|
||||||
@rem ##########################################################################
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
@rem Set local scope for the variables with windows NT shell
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
set DEFAULT_JVM_OPTS=
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
set DIRNAME=%~dp0
|
@rem limitations under the License.
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
@rem
|
||||||
set APP_BASE_NAME=%~n0
|
|
||||||
set APP_HOME=%DIRNAME%
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
@rem Find java.exe
|
@rem
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
set JAVA_EXE=java.exe
|
@rem ##########################################################################
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
set DIRNAME=%~dp0
|
||||||
echo.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
@rem This is normally unused
|
||||||
echo location of your Java installation.
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
goto fail
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
:findJavaFromJavaHome
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
@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"
|
||||||
if exist "%JAVA_EXE%" goto init
|
|
||||||
|
@rem Find java.exe
|
||||||
echo.
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
|
||||||
echo.
|
set JAVA_EXE=java.exe
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
echo location of your Java installation.
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
goto fail
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
:init
|
echo.
|
||||||
@rem Get command-line arguments, handling Windowz variants
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
goto fail
|
||||||
|
|
||||||
:win9xME_args
|
:findJavaFromJavaHome
|
||||||
@rem Slurp the command line arguments.
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set CMD_LINE_ARGS=
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
set _SKIP=2
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
set CMD_LINE_ARGS=%*
|
echo.
|
||||||
goto execute
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
:4NT_args
|
|
||||||
@rem Get arguments from the 4NT Shell from JP Software
|
goto fail
|
||||||
set CMD_LINE_ARGS=%$
|
|
||||||
|
:execute
|
||||||
:execute
|
@rem Setup the command line
|
||||||
@rem Setup the command line
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
:mainEnd
|
exit /b %EXIT_CODE%
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
:mainEnd
|
||||||
:omega
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
apply plugin: 'application'
|
|
||||||
|
|
||||||
mainClassName = 'jadx.cli.JadxCLI'
|
|
||||||
applicationName = 'jadx'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile(project(':jadx-core'))
|
|
||||||
compile 'com.beust:jcommander:1.30'
|
|
||||||
}
|
|
||||||
|
|
||||||
startScripts {
|
|
||||||
doLast {
|
|
||||||
// increase default max heap size
|
|
||||||
String var = 'DEFAULT_JVM_OPTS='
|
|
||||||
String args = '-Xmx1300M'
|
|
||||||
unixScript.text = unixScript.text.replace(var + '""', var + '"' + args + '"')
|
|
||||||
windowsScript.text = windowsScript.text.replace(var, var + args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationDistribution.with {
|
|
||||||
into('') {
|
|
||||||
from '../.'
|
|
||||||
include 'README.md'
|
|
||||||
include 'NOTICE'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
plugins {
|
||||||
|
id("jadx-java")
|
||||||
|
id("application")
|
||||||
|
|
||||||
|
// use shadow only for application scripts, jar will be copied from jadx-gui
|
||||||
|
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":jadx-core"))
|
||||||
|
implementation(project(":jadx-plugins-tools"))
|
||||||
|
|
||||||
|
runtimeOnly(project(":jadx-plugins:jadx-dex-input"))
|
||||||
|
runtimeOnly(project(":jadx-plugins:jadx-java-input"))
|
||||||
|
runtimeOnly(project(":jadx-plugins:jadx-java-convert"))
|
||||||
|
runtimeOnly(project(":jadx-plugins:jadx-smali-input"))
|
||||||
|
runtimeOnly(project(":jadx-plugins:jadx-rename-mappings"))
|
||||||
|
runtimeOnly(project(":jadx-plugins:jadx-kotlin-metadata"))
|
||||||
|
runtimeOnly(project(":jadx-plugins:jadx-script:jadx-script-plugin"))
|
||||||
|
runtimeOnly(project(":jadx-plugins:jadx-xapk-input"))
|
||||||
|
|
||||||
|
implementation("org.jcommander:jcommander:1.83")
|
||||||
|
implementation("ch.qos.logback:logback-classic:1.4.14")
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
applicationName = "jadx"
|
||||||
|
mainClass.set("jadx.cli.JadxCLI")
|
||||||
|
applicationDefaultJvmArgs =
|
||||||
|
listOf(
|
||||||
|
"-Xms256M",
|
||||||
|
"-XX:MaxRAMPercentage=70.0",
|
||||||
|
// disable zip checks (#1962)
|
||||||
|
"-Djdk.util.zip.disableZip64ExtraFieldValidation=true",
|
||||||
|
)
|
||||||
|
applicationDistribution.from("$rootDir") {
|
||||||
|
include("README.md")
|
||||||
|
include("NOTICE")
|
||||||
|
include("LICENSE")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.shadowJar {
|
||||||
|
// shadow jar not needed
|
||||||
|
configurations = listOf()
|
||||||
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
package jadx.cli;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
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.ParameterDescription;
|
||||||
|
import com.beust.jcommander.ParameterException;
|
||||||
|
import com.beust.jcommander.Parameterized;
|
||||||
|
|
||||||
|
import jadx.api.JadxArgs;
|
||||||
|
import jadx.api.JadxDecompiler;
|
||||||
|
import jadx.api.plugins.JadxPluginInfo;
|
||||||
|
import jadx.api.plugins.options.JadxPluginOptions;
|
||||||
|
import jadx.api.plugins.options.OptionDescription;
|
||||||
|
import jadx.core.plugins.JadxPluginManager;
|
||||||
|
import jadx.core.plugins.PluginContext;
|
||||||
|
import jadx.core.utils.Utils;
|
||||||
|
import jadx.plugins.tools.JadxExternalPluginsLoader;
|
||||||
|
|
||||||
|
public class JCommanderWrapper<T> {
|
||||||
|
private final JCommander jc;
|
||||||
|
private final JadxCLIArgs argsObj;
|
||||||
|
|
||||||
|
public JCommanderWrapper(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);
|
||||||
|
return true;
|
||||||
|
} catch (ParameterException e) {
|
||||||
|
System.err.println("Arguments parse error: " + e.getMessage());
|
||||||
|
printUsage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean processCommands() {
|
||||||
|
String parsedCommand = jc.getParsedCommand();
|
||||||
|
if (parsedCommand == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return JadxCLICommands.process(this, jc, parsedCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void overrideProvided(JadxCLIArgs obj) {
|
||||||
|
List<ParameterDescription> fieldsParams = jc.getParameters();
|
||||||
|
List<ParameterDescription> parameters = new ArrayList<>(1 + fieldsParams.size());
|
||||||
|
parameters.add(jc.getMainParameterValue());
|
||||||
|
parameters.addAll(fieldsParams);
|
||||||
|
for (ParameterDescription parameter : parameters) {
|
||||||
|
if (parameter.isAssigned()) {
|
||||||
|
// copy assigned field value to obj
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getUnknownOptions() {
|
||||||
|
return jc.getUnknownOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printUsage() {
|
||||||
|
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 [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_ZIP_SECURITY - set to 'true' to disable all security checks for zip files");
|
||||||
|
out.println(" JADX_ZIP_MAX_ENTRIES_COUNT - maximum allowed number of entries in zip files (default: 100 000)");
|
||||||
|
out.println(" JADX_TMP_DIR - custom temp directory, using system by default");
|
||||||
|
out.println();
|
||||||
|
out.println("Examples:");
|
||||||
|
out.println(" jadx -d out classes.dex");
|
||||||
|
out.println(" jadx --rename-flags \"none\" classes.dex");
|
||||||
|
out.println(" jadx --rename-flags \"valid, printable\" classes.dex");
|
||||||
|
out.println(" jadx --log-level ERROR app.apk");
|
||||||
|
out.println(" jadx -Pdex-input.verify-checksum=no app.apk");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printUsage(JCommander subCommander) {
|
||||||
|
PrintStream out = System.out;
|
||||||
|
out.println("usage: " + subCommander.getProgramName() + " [options]");
|
||||||
|
printOptions(subCommander, out, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int printOptions(JCommander jc, PrintStream out, boolean addDefaults) {
|
||||||
|
out.println("options:");
|
||||||
|
|
||||||
|
List<ParameterDescription> params = jc.getParameters();
|
||||||
|
Map<String, ParameterDescription> paramsMap = new LinkedHashMap<>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maxNamesLen += 3;
|
||||||
|
|
||||||
|
Object args = jc.getObjects().get(0);
|
||||||
|
for (Field f : getFields(args.getClass())) {
|
||||||
|
String name = f.getName();
|
||||||
|
ParameterDescription p = paramsMap.get(name);
|
||||||
|
if (p == null || p.getParameter().hidden()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
StringBuilder opt = new StringBuilder();
|
||||||
|
opt.append(" ").append(p.getNames());
|
||||||
|
String description = p.getDescription();
|
||||||
|
addSpaces(opt, maxNamesLen - opt.length());
|
||||||
|
if (description.contains("\n")) {
|
||||||
|
String[] lines = description.split("\n");
|
||||||
|
opt.append("- ").append(lines[0]);
|
||||||
|
for (int i = 1; i < lines.length; i++) {
|
||||||
|
opt.append('\n');
|
||||||
|
addSpaces(opt, maxNamesLen + 2);
|
||||||
|
opt.append(lines[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opt.append("- ").append(description);
|
||||||
|
}
|
||||||
|
if (addDefaults) {
|
||||||
|
String defaultValue = getDefaultValue(args, f, opt);
|
||||||
|
if (defaultValue != null && !description.contains("(default)")) {
|
||||||
|
opt.append(", default: ").append(defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.println(opt);
|
||||||
|
}
|
||||||
|
return maxNamesLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all declared fields of the specified class and all super classes
|
||||||
|
*/
|
||||||
|
private static List<Field> getFields(Class<?> clazz) {
|
||||||
|
List<Field> fieldList = new ArrayList<>();
|
||||||
|
while (clazz != null) {
|
||||||
|
fieldList.addAll(Arrays.asList(clazz.getDeclaredFields()));
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
return fieldList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static String getDefaultValue(Object args, Field f, StringBuilder opt) {
|
||||||
|
try {
|
||||||
|
Class<?> fieldType = f.getType();
|
||||||
|
if (fieldType == int.class) {
|
||||||
|
return Integer.toString(f.getInt(args));
|
||||||
|
}
|
||||||
|
if (fieldType == String.class) {
|
||||||
|
return (String) f.get(args);
|
||||||
|
}
|
||||||
|
if (Enum.class.isAssignableFrom(fieldType)) {
|
||||||
|
Enum<?> val = (Enum<?>) f.get(args);
|
||||||
|
if (val != null) {
|
||||||
|
return val.name().toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSpaces(StringBuilder str, int count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
str.append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String appendPluginOptions(int maxNamesLen) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int k = 1;
|
||||||
|
// load and init all options plugins to print all options
|
||||||
|
try (JadxDecompiler decompiler = new JadxDecompiler(new JadxArgs())) {
|
||||||
|
JadxPluginManager pluginManager = decompiler.getPluginManager();
|
||||||
|
pluginManager.load(new JadxExternalPluginsLoader());
|
||||||
|
pluginManager.initAll();
|
||||||
|
for (PluginContext context : pluginManager.getAllPluginContexts()) {
|
||||||
|
JadxPluginOptions options = context.getOptions();
|
||||||
|
if (options != null) {
|
||||||
|
if (appendPlugin(context.getPluginInfo(), context.getOptions(), sb, maxNamesLen, k)) {
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sb.length() == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return "\nPlugin options (-P<name>=<value>):" + sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean appendPlugin(JadxPluginInfo pluginInfo, JadxPluginOptions options, StringBuilder out, int maxNamesLen, int k) {
|
||||||
|
List<OptionDescription> descs = options.getOptionsDescriptions();
|
||||||
|
if (descs.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out.append("\n ").append(k).append(") ");
|
||||||
|
out.append(pluginInfo.getPluginId()).append(": ").append(pluginInfo.getDescription());
|
||||||
|
for (OptionDescription desc : descs) {
|
||||||
|
StringBuilder opt = new StringBuilder();
|
||||||
|
opt.append(" - ").append(desc.name());
|
||||||
|
addSpaces(opt, maxNamesLen - opt.length());
|
||||||
|
opt.append("- ").append(desc.description());
|
||||||
|
if (!desc.values().isEmpty()) {
|
||||||
|
opt.append(", values: ").append(desc.values());
|
||||||
|
}
|
||||||
|
if (desc.defaultValue() != null) {
|
||||||
|
opt.append(", default: ").append(desc.defaultValue());
|
||||||
|
}
|
||||||
|
out.append("\n").append(opt);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,68 +1,114 @@
|
|||||||
package jadx.cli;
|
package jadx.cli;
|
||||||
|
|
||||||
import jadx.api.Decompiler;
|
|
||||||
import jadx.core.utils.ErrorsCounter;
|
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.JadxArgs;
|
||||||
|
import jadx.api.JadxDecompiler;
|
||||||
|
import jadx.api.impl.AnnotatedCodeWriter;
|
||||||
|
import jadx.api.impl.NoOpCodeCache;
|
||||||
|
import jadx.api.impl.SimpleCodeWriter;
|
||||||
|
import jadx.cli.LogHelper.LogLevelEnum;
|
||||||
|
import jadx.core.utils.exceptions.JadxArgsValidateException;
|
||||||
|
import jadx.core.utils.files.FileUtils;
|
||||||
|
import jadx.plugins.tools.JadxExternalPluginsLoader;
|
||||||
|
|
||||||
public class JadxCLI {
|
public class JadxCLI {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
|
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
int result = 0;
|
||||||
try {
|
try {
|
||||||
JadxCLIArgs jadxArgs = new JadxCLIArgs(args);
|
result = execute(args);
|
||||||
checkArgs(jadxArgs);
|
} catch (JadxArgsValidateException e) {
|
||||||
processAndSave(jadxArgs);
|
LOG.error("Incorrect arguments: {}", e.getMessage());
|
||||||
} catch (Exception e) {
|
result = 1;
|
||||||
LOG.error(e.getMessage());
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void processAndSave(JadxCLIArgs jadxArgs) {
|
|
||||||
try {
|
|
||||||
Decompiler jadx = new Decompiler(jadxArgs);
|
|
||||||
jadx.loadFiles(jadxArgs.getInput());
|
|
||||||
jadx.setOutputDir(jadxArgs.getOutDir());
|
|
||||||
jadx.save();
|
|
||||||
LOG.info("done");
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
LOG.error("jadx error:", e);
|
LOG.error("Process error:", e);
|
||||||
|
result = 1;
|
||||||
|
} finally {
|
||||||
|
FileUtils.deleteTempRootDir();
|
||||||
|
System.exit(result);
|
||||||
}
|
}
|
||||||
int errorsCount = ErrorsCounter.getErrorCount();
|
|
||||||
if (errorsCount != 0) {
|
|
||||||
ErrorsCounter.printReport();
|
|
||||||
}
|
|
||||||
System.exit(errorsCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkArgs(JadxCLIArgs jadxArgs) throws JadxException {
|
public static int execute(String[] args) {
|
||||||
if (jadxArgs.getInput().isEmpty()) {
|
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||||
LOG.error("Please specify input file");
|
if (jadxArgs.processArgs(args)) {
|
||||||
jadxArgs.printUsage();
|
return processAndSave(jadxArgs);
|
||||||
System.exit(1);
|
|
||||||
}
|
}
|
||||||
File outputDir = jadxArgs.getOutDir();
|
return 0;
|
||||||
if (outputDir == null) {
|
}
|
||||||
String outDirName;
|
|
||||||
File file = jadxArgs.getInput().get(0);
|
private static int processAndSave(JadxCLIArgs cliArgs) {
|
||||||
String name = file.getName();
|
LogHelper.initLogLevel(cliArgs);
|
||||||
int pos = name.lastIndexOf('.');
|
LogHelper.setLogLevelsForLoadingStage();
|
||||||
if (pos != -1) {
|
JadxArgs jadxArgs = cliArgs.toJadxArgs();
|
||||||
outDirName = name.substring(0, pos);
|
jadxArgs.setCodeCache(new NoOpCodeCache());
|
||||||
} else {
|
jadxArgs.setPluginLoader(new JadxExternalPluginsLoader());
|
||||||
outDirName = name + "-jadx-out";
|
initCodeWriterProvider(jadxArgs);
|
||||||
|
try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
|
||||||
|
jadx.load();
|
||||||
|
if (checkForErrors(jadx)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
LogHelper.setLogLevelsForDecompileStage();
|
||||||
|
if (!SingleClassMode.process(jadx, cliArgs)) {
|
||||||
|
save(jadx);
|
||||||
|
}
|
||||||
|
int errorsCount = jadx.getErrorsCount();
|
||||||
|
if (errorsCount != 0) {
|
||||||
|
jadx.printErrorsReport();
|
||||||
|
LOG.error("finished with errors, count: {}", errorsCount);
|
||||||
|
} else {
|
||||||
|
LOG.info("done");
|
||||||
}
|
}
|
||||||
LOG.info("output directory: " + outDirName);
|
|
||||||
outputDir = new File(outDirName);
|
|
||||||
jadxArgs.setOutputDir(outputDir);
|
|
||||||
}
|
}
|
||||||
if (outputDir.exists() && !outputDir.isDirectory()) {
|
return 0;
|
||||||
throw new JadxException("Output directory exists as file " + outputDir);
|
}
|
||||||
|
|
||||||
|
private static void initCodeWriterProvider(JadxArgs jadxArgs) {
|
||||||
|
switch (jadxArgs.getOutputFormat()) {
|
||||||
|
case JAVA:
|
||||||
|
jadxArgs.setCodeWriterProvider(SimpleCodeWriter::new);
|
||||||
|
break;
|
||||||
|
case JSON:
|
||||||
|
// needed for code offsets and source lines
|
||||||
|
jadxArgs.setCodeWriterProvider(AnnotatedCodeWriter::new);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkForErrors(JadxDecompiler jadx) {
|
||||||
|
if (jadx.getRoot().getClasses().isEmpty()) {
|
||||||
|
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");
|
||||||
|
// continue processing
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void save(JadxDecompiler jadx) {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
// dumb line clear :)
|
||||||
|
System.out.print(" \r");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,184 +1,644 @@
|
|||||||
package jadx.cli;
|
package jadx.cli;
|
||||||
|
|
||||||
import jadx.api.IJadxArgs;
|
import java.nio.file.Path;
|
||||||
import jadx.core.Consts;
|
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import com.beust.jcommander.DynamicParameter;
|
||||||
import org.slf4j.LoggerFactory;
|
import com.beust.jcommander.IStringConverter;
|
||||||
|
|
||||||
import com.beust.jcommander.JCommander;
|
|
||||||
import com.beust.jcommander.Parameter;
|
import com.beust.jcommander.Parameter;
|
||||||
import com.beust.jcommander.ParameterDescription;
|
|
||||||
import com.beust.jcommander.ParameterException;
|
|
||||||
|
|
||||||
public final class JadxCLIArgs implements IJadxArgs {
|
import jadx.api.CommentsLevel;
|
||||||
|
import jadx.api.DecompilationMode;
|
||||||
|
import jadx.api.JadxArgs;
|
||||||
|
import jadx.api.JadxArgs.RenameEnum;
|
||||||
|
import jadx.api.JadxArgs.UseKotlinMethodsForVarNames;
|
||||||
|
import jadx.api.JadxDecompiler;
|
||||||
|
import jadx.api.args.GeneratedRenamesMappingFileMode;
|
||||||
|
import jadx.api.args.IntegerFormat;
|
||||||
|
import jadx.api.args.ResourceNameSource;
|
||||||
|
import jadx.api.args.UserRenamesMappingsMode;
|
||||||
|
import jadx.core.deobf.conditions.DeobfWhitelist;
|
||||||
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
|
import jadx.core.utils.files.FileUtils;
|
||||||
|
|
||||||
@Parameter(description = "<input file> (.dex, .apk or .jar)")
|
public class JadxCLIArgs {
|
||||||
protected List<String> files;
|
|
||||||
|
|
||||||
@Parameter(names = {"-d", "--output-dir"}, description = "output directory")
|
@Parameter(description = "<input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk)")
|
||||||
protected String outDirName;
|
protected List<String> files = new ArrayList<>(1);
|
||||||
|
|
||||||
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
|
@Parameter(names = { "-d", "--output-dir" }, description = "output directory")
|
||||||
protected int threadsCount = Runtime.getRuntime().availableProcessors();
|
protected String outDir;
|
||||||
|
|
||||||
@Parameter(names = {"-f", "--fallback"}, description = "make simple dump (using goto instead of 'if', 'for', etc)", help = true)
|
@Parameter(names = { "-ds", "--output-dir-src" }, description = "output directory for sources")
|
||||||
protected boolean fallbackMode = false;
|
protected String outDirSrc;
|
||||||
|
|
||||||
@Parameter(names = {"--cfg"}, description = "save methods control flow graph to dot file")
|
@Parameter(names = { "-dr", "--output-dir-res" }, description = "output directory for resources")
|
||||||
|
protected String outDirRes;
|
||||||
|
|
||||||
|
@Parameter(names = { "-r", "--no-res" }, description = "do not decode resources")
|
||||||
|
protected boolean skipResources = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "-s", "--no-src" }, description = "do not decompile source code")
|
||||||
|
protected boolean skipSources = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "--single-class" }, description = "decompile a single class, full name, raw or alias")
|
||||||
|
protected String singleClass = null;
|
||||||
|
|
||||||
|
@Parameter(names = { "--single-class-output" }, description = "file or dir for write if decompile a single class")
|
||||||
|
protected String singleClassOutput = null;
|
||||||
|
|
||||||
|
@Parameter(names = { "--output-format" }, description = "can be 'java' or 'json'")
|
||||||
|
protected String outputFormat = "java";
|
||||||
|
|
||||||
|
@Parameter(names = { "-e", "--export-gradle" }, description = "save as android gradle project")
|
||||||
|
protected boolean exportAsGradleProject = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "-j", "--threads-count" }, description = "processing threads count")
|
||||||
|
protected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "-m", "--decompilation-mode" },
|
||||||
|
description = "code output mode:"
|
||||||
|
+ "\n 'auto' - trying best options (default)"
|
||||||
|
+ "\n 'restructure' - restore code structure (normal java code)"
|
||||||
|
+ "\n 'simple' - simplified instructions (linear, with goto's)"
|
||||||
|
+ "\n 'fallback' - raw instructions without modifications",
|
||||||
|
converter = DecompilationModeConverter.class
|
||||||
|
)
|
||||||
|
protected DecompilationMode decompilationMode = DecompilationMode.AUTO;
|
||||||
|
|
||||||
|
@Parameter(names = { "--show-bad-code" }, description = "show inconsistent code (incorrectly decompiled)")
|
||||||
|
protected boolean showInconsistentCode = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "--no-xml-pretty-print" }, description = "do not prettify XML")
|
||||||
|
protected boolean skipXmlPrettyPrint = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "--no-imports" }, description = "disable use of imports, always write entire package name")
|
||||||
|
protected boolean useImports = true;
|
||||||
|
|
||||||
|
@Parameter(names = { "--no-debug-info" }, description = "disable debug info parsing and processing")
|
||||||
|
protected boolean debugInfo = true;
|
||||||
|
|
||||||
|
@Parameter(names = { "--add-debug-lines" }, description = "add comments with debug line numbers if available")
|
||||||
|
protected boolean addDebugLines = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "--no-inline-anonymous" }, description = "disable anonymous classes inline")
|
||||||
|
protected boolean inlineAnonymousClasses = true;
|
||||||
|
|
||||||
|
@Parameter(names = { "--no-inline-methods" }, description = "disable methods inline")
|
||||||
|
protected boolean inlineMethods = true;
|
||||||
|
|
||||||
|
@Parameter(names = { "--no-move-inner-classes" }, description = "disable move inner classes into parent")
|
||||||
|
protected boolean moveInnerClasses = true;
|
||||||
|
|
||||||
|
@Parameter(names = { "--no-inline-kotlin-lambda" }, description = "disable inline for Kotlin lambdas")
|
||||||
|
protected boolean allowInlineKotlinLambda = true;
|
||||||
|
|
||||||
|
@Parameter(names = "--no-finally", description = "don't extract finally block")
|
||||||
|
protected boolean extractFinally = true;
|
||||||
|
|
||||||
|
@Parameter(names = "--no-replace-consts", description = "don't replace constant value with matching constant field")
|
||||||
|
protected boolean replaceConsts = true;
|
||||||
|
|
||||||
|
@Parameter(names = { "--escape-unicode" }, description = "escape non latin characters in strings (with \\u)")
|
||||||
|
protected boolean escapeUnicode = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "--respect-bytecode-access-modifiers" }, description = "don't change original access modifiers")
|
||||||
|
protected boolean respectBytecodeAccessModifiers = false;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--mappings-path" },
|
||||||
|
description = "deobfuscation mappings file or directory. Allowed formats: Tiny and Tiny v2 (both '.tiny'), Enigma (.mapping) or Enigma directory"
|
||||||
|
)
|
||||||
|
protected Path userRenamesMappingsPath;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--mappings-mode" },
|
||||||
|
description = "set mode for handling the deobfuscation mapping file:"
|
||||||
|
+ "\n 'read' - just read, user can always save manually (default)"
|
||||||
|
+ "\n 'read-and-autosave-every-change' - read and autosave after every change"
|
||||||
|
+ "\n 'read-and-autosave-before-closing' - read and autosave before exiting the app or closing the project"
|
||||||
|
+ "\n 'ignore' - don't read or save (can be used to skip loading mapping files referenced in the project file)"
|
||||||
|
)
|
||||||
|
protected UserRenamesMappingsMode userRenamesMappingsMode = UserRenamesMappingsMode.getDefault();
|
||||||
|
|
||||||
|
@Parameter(names = { "--deobf" }, description = "activate deobfuscation")
|
||||||
|
protected boolean deobfuscationOn = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "--deobf-min" }, description = "min length of name, renamed if shorter")
|
||||||
|
protected int deobfuscationMinLength = 3;
|
||||||
|
|
||||||
|
@Parameter(names = { "--deobf-max" }, description = "max length of name, renamed if longer")
|
||||||
|
protected int deobfuscationMaxLength = 64;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--deobf-whitelist" },
|
||||||
|
description = "space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation"
|
||||||
|
)
|
||||||
|
protected String deobfuscationWhitelistStr = DeobfWhitelist.DEFAULT_STR;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--deobf-cfg-file" },
|
||||||
|
description = "deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format),"
|
||||||
|
+ " default: same dir and name as input file with '.jobf' extension"
|
||||||
|
)
|
||||||
|
protected String generatedRenamesMappingFile;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--deobf-cfg-file-mode" },
|
||||||
|
description = "set mode for handling the JADX auto-generated names' deobfuscation map file:"
|
||||||
|
+ "\n 'read' - read if found, don't save (default)"
|
||||||
|
+ "\n 'read-or-save' - read if found, save otherwise (don't overwrite)"
|
||||||
|
+ "\n 'overwrite' - don't read, always save"
|
||||||
|
+ "\n 'ignore' - don't read and don't save",
|
||||||
|
converter = DeobfuscationMapFileModeConverter.class
|
||||||
|
)
|
||||||
|
protected GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.getDefault();
|
||||||
|
|
||||||
|
@Parameter(names = { "--deobf-use-sourcename" }, description = "use source file name as class name alias")
|
||||||
|
protected boolean deobfuscationUseSourceNameAsAlias = false;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--deobf-res-name-source" },
|
||||||
|
description = "better name source for resources:"
|
||||||
|
+ "\n 'auto' - automatically select best name (default)"
|
||||||
|
+ "\n 'resources' - use resources names"
|
||||||
|
+ "\n 'code' - use R class fields names",
|
||||||
|
converter = ResourceNameSourceConverter.class
|
||||||
|
)
|
||||||
|
protected ResourceNameSource resourceNameSource = ResourceNameSource.AUTO;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--use-kotlin-methods-for-var-names" },
|
||||||
|
description = "use kotlin intrinsic methods to rename variables, values: disable, apply, apply-and-hide",
|
||||||
|
converter = UseKotlinMethodsForVarNamesConverter.class
|
||||||
|
)
|
||||||
|
protected UseKotlinMethodsForVarNames useKotlinMethodsForVarNames = UseKotlinMethodsForVarNames.APPLY;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--rename-flags" },
|
||||||
|
description = "fix options (comma-separated list of):"
|
||||||
|
+ "\n 'case' - fix case sensitivity issues (according to --fs-case-sensitive option),"
|
||||||
|
+ "\n 'valid' - rename java identifiers to make them valid,"
|
||||||
|
+ "\n 'printable' - remove non-printable chars from identifiers,"
|
||||||
|
+ "\nor single 'none' - to disable all renames"
|
||||||
|
+ "\nor single 'all' - to enable all (default)",
|
||||||
|
converter = RenameConverter.class
|
||||||
|
)
|
||||||
|
protected Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class);
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--integer-format" },
|
||||||
|
description = "how integers are displayed:"
|
||||||
|
+ "\n 'auto' - automatically select (default)"
|
||||||
|
+ "\n 'decimal' - use decimal"
|
||||||
|
+ "\n 'hexadecimal' - use hexadecimal",
|
||||||
|
converter = IntegerFormatConverter.class
|
||||||
|
)
|
||||||
|
protected IntegerFormat integerFormat = IntegerFormat.AUTO;
|
||||||
|
|
||||||
|
@Parameter(names = { "--fs-case-sensitive" }, description = "treat filesystem as case sensitive, false by default")
|
||||||
|
protected boolean fsCaseSensitive = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "--cfg" }, description = "save methods control flow graph to dot file")
|
||||||
protected boolean cfgOutput = false;
|
protected boolean cfgOutput = false;
|
||||||
|
|
||||||
@Parameter(names = {"--raw-cfg"}, description = "save methods control flow graph (use raw instructions)")
|
@Parameter(names = { "--raw-cfg" }, description = "save methods control flow graph (use raw instructions)")
|
||||||
protected boolean rawCfgOutput = false;
|
protected boolean rawCfgOutput = false;
|
||||||
|
|
||||||
@Parameter(names = {"-v", "--verbose"}, description = "verbose output")
|
@Parameter(names = { "-f", "--fallback" }, description = "set '--decompilation-mode' to 'fallback' (deprecated)")
|
||||||
|
protected boolean fallbackMode = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "--use-dx" }, description = "use dx/d8 to convert java bytecode")
|
||||||
|
protected boolean useDx = false;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--comments-level" },
|
||||||
|
description = "set code comments level, values: error, warn, info, debug, user-only, none",
|
||||||
|
converter = CommentsLevelConverter.class
|
||||||
|
)
|
||||||
|
protected CommentsLevel commentsLevel = CommentsLevel.INFO;
|
||||||
|
|
||||||
|
@Parameter(
|
||||||
|
names = { "--log-level" },
|
||||||
|
description = "set log level, values: quiet, progress, error, warn, info, debug",
|
||||||
|
converter = LogLevelConverter.class
|
||||||
|
)
|
||||||
|
protected LogHelper.LogLevelEnum logLevel = LogHelper.LogLevelEnum.PROGRESS;
|
||||||
|
|
||||||
|
@Parameter(names = { "-v", "--verbose" }, description = "verbose output (set --log-level to DEBUG)")
|
||||||
protected boolean verbose = false;
|
protected boolean verbose = false;
|
||||||
|
|
||||||
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
|
@Parameter(names = { "-q", "--quiet" }, description = "turn off output (set --log-level to QUIET)")
|
||||||
|
protected boolean quiet = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "--version" }, description = "print jadx version")
|
||||||
|
protected boolean printVersion = false;
|
||||||
|
|
||||||
|
@Parameter(names = { "-h", "--help" }, description = "print this help", help = true)
|
||||||
protected boolean printHelp = false;
|
protected boolean printHelp = false;
|
||||||
|
|
||||||
private final List<File> input = new ArrayList<File>(1);
|
@DynamicParameter(names = "-P", description = "Plugin options", hidden = true)
|
||||||
private File outputDir;
|
protected Map<String, String> pluginOptions = new HashMap<>();
|
||||||
|
|
||||||
public JadxCLIArgs(String[] args) {
|
public boolean processArgs(String[] args) {
|
||||||
parse(args);
|
JCommanderWrapper<JadxCLIArgs> jcw = new JCommanderWrapper<>(this);
|
||||||
processArgs();
|
return jcw.parse(args) && process(jcw);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parse(String[] args) {
|
/**
|
||||||
try {
|
* Set values only for options provided in cmd.
|
||||||
new JCommander(this, args);
|
* Used to merge saved options and options passed in command line.
|
||||||
} catch (ParameterException e) {
|
*/
|
||||||
System.err.println("Arguments parse error: " + e.getMessage());
|
public boolean overrideProvided(String[] args) {
|
||||||
printUsage();
|
JCommanderWrapper<JadxCLIArgs> jcw = new JCommanderWrapper<>(newInstance());
|
||||||
System.exit(1);
|
if (!jcw.parse(args)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
jcw.overrideProvided(this);
|
||||||
|
return process(jcw);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processArgs() {
|
protected JadxCLIArgs newInstance() {
|
||||||
if (isPrintHelp()) {
|
return new JadxCLIArgs();
|
||||||
printUsage();
|
}
|
||||||
System.exit(0);
|
|
||||||
|
private boolean process(JCommanderWrapper<JadxCLIArgs> jcw) {
|
||||||
|
files.addAll(jcw.getUnknownOptions());
|
||||||
|
if (jcw.processCommands()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (printHelp) {
|
||||||
|
jcw.printUsage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (printVersion) {
|
||||||
|
System.out.println(JadxDecompiler.getVersion());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (threadsCount <= 0)
|
if (threadsCount <= 0) {
|
||||||
throw new JadxException("Threads count must be positive");
|
throw new JadxException("Threads count must be positive, got: " + threadsCount);
|
||||||
|
|
||||||
if (files != null) {
|
|
||||||
for (String fileName : files) {
|
|
||||||
File file = new File(fileName);
|
|
||||||
if (file.exists())
|
|
||||||
input.add(file);
|
|
||||||
else
|
|
||||||
throw new JadxException("File not found: " + file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.size() > 1)
|
|
||||||
throw new JadxException("Only one input file is supported");
|
|
||||||
|
|
||||||
if (outDirName != null)
|
|
||||||
outputDir = new File(outDirName);
|
|
||||||
|
|
||||||
if (isVerbose()) {
|
|
||||||
ch.qos.logback.classic.Logger rootLogger =
|
|
||||||
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
|
|
||||||
rootLogger.setLevel(ch.qos.logback.classic.Level.DEBUG);
|
|
||||||
}
|
}
|
||||||
} catch (JadxException e) {
|
} catch (JadxException e) {
|
||||||
System.err.println("ERROR: " + e.getMessage());
|
System.err.println("ERROR: " + e.getMessage());
|
||||||
printUsage();
|
jcw.printUsage();
|
||||||
System.exit(1);
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void printUsage() {
|
public JadxArgs toJadxArgs() {
|
||||||
JCommander jc = new JCommander(this);
|
JadxArgs args = new JadxArgs();
|
||||||
// print usage in not sorted fields order (by default its sorted by description)
|
args.setInputFiles(files.stream().map(FileUtils::toFile).collect(Collectors.toList()));
|
||||||
PrintStream out = System.out;
|
args.setOutDir(FileUtils.toFile(outDir));
|
||||||
out.println();
|
args.setOutDirSrc(FileUtils.toFile(outDirSrc));
|
||||||
out.println("jadx - dex to java decompiler, version: " + Consts.JADX_VERSION);
|
args.setOutDirRes(FileUtils.toFile(outDirRes));
|
||||||
out.println();
|
args.setOutputFormat(JadxArgs.OutputFormatEnum.valueOf(outputFormat.toUpperCase()));
|
||||||
out.println("usage: jadx [options] " + jc.getMainParameterDescription());
|
args.setThreadsCount(threadsCount);
|
||||||
out.println("options:");
|
args.setSkipSources(skipSources);
|
||||||
|
args.setSkipResources(skipResources);
|
||||||
List<ParameterDescription> params = jc.getParameters();
|
if (fallbackMode) {
|
||||||
|
args.setDecompilationMode(DecompilationMode.FALLBACK);
|
||||||
int maxNamesLen = 0;
|
} else {
|
||||||
for (ParameterDescription p : params) {
|
args.setDecompilationMode(decompilationMode);
|
||||||
int len = p.getNames().length();
|
|
||||||
if (len > maxNamesLen)
|
|
||||||
maxNamesLen = len;
|
|
||||||
}
|
}
|
||||||
|
args.setShowInconsistentCode(showInconsistentCode);
|
||||||
Field[] fields = this.getClass().getDeclaredFields();
|
args.setCfgOutput(cfgOutput);
|
||||||
for (Field f : fields) {
|
args.setRawCFGOutput(rawCfgOutput);
|
||||||
for (ParameterDescription p : params) {
|
args.setReplaceConsts(replaceConsts);
|
||||||
String name = f.getName();
|
if (userRenamesMappingsPath != null) {
|
||||||
if (name.equals(p.getParameterized().getName())) {
|
args.setUserRenamesMappingsPath(userRenamesMappingsPath);
|
||||||
StringBuilder opt = new StringBuilder();
|
|
||||||
opt.append(' ').append(p.getNames());
|
|
||||||
addSpaces(opt, maxNamesLen - opt.length() + 2);
|
|
||||||
opt.append("- ").append(p.getDescription());
|
|
||||||
out.println(opt.toString());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
out.println("Example:");
|
args.setUserRenamesMappingsMode(userRenamesMappingsMode);
|
||||||
out.println(" jadx -d out classes.dex");
|
args.setDeobfuscationOn(deobfuscationOn);
|
||||||
|
args.setGeneratedRenamesMappingFile(FileUtils.toFile(generatedRenamesMappingFile));
|
||||||
|
args.setGeneratedRenamesMappingFileMode(generatedRenamesMappingFileMode);
|
||||||
|
args.setDeobfuscationMinLength(deobfuscationMinLength);
|
||||||
|
args.setDeobfuscationMaxLength(deobfuscationMaxLength);
|
||||||
|
args.setDeobfuscationWhitelist(Arrays.asList(deobfuscationWhitelistStr.split(" ")));
|
||||||
|
args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias);
|
||||||
|
args.setUseKotlinMethodsForVarNames(useKotlinMethodsForVarNames);
|
||||||
|
args.setResourceNameSource(resourceNameSource);
|
||||||
|
args.setEscapeUnicode(escapeUnicode);
|
||||||
|
args.setRespectBytecodeAccModifiers(respectBytecodeAccessModifiers);
|
||||||
|
args.setExportAsGradleProject(exportAsGradleProject);
|
||||||
|
args.setSkipXmlPrettyPrint(skipXmlPrettyPrint);
|
||||||
|
args.setUseImports(useImports);
|
||||||
|
args.setDebugInfo(debugInfo);
|
||||||
|
args.setInsertDebugLines(addDebugLines);
|
||||||
|
args.setInlineAnonymousClasses(inlineAnonymousClasses);
|
||||||
|
args.setInlineMethods(inlineMethods);
|
||||||
|
args.setMoveInnerClasses(moveInnerClasses);
|
||||||
|
args.setAllowInlineKotlinLambda(allowInlineKotlinLambda);
|
||||||
|
args.setExtractFinally(extractFinally);
|
||||||
|
args.setRenameFlags(renameFlags);
|
||||||
|
args.setFsCaseSensitive(fsCaseSensitive);
|
||||||
|
args.setCommentsLevel(commentsLevel);
|
||||||
|
args.setIntegerFormat(integerFormat);
|
||||||
|
args.setUseDxInput(useDx);
|
||||||
|
args.setPluginOptions(pluginOptions);
|
||||||
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addSpaces(StringBuilder str, int count) {
|
public List<String> getFiles() {
|
||||||
for (int i = 0; i < count; i++)
|
return files;
|
||||||
str.append(' ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<File> getInput() {
|
public String getOutDir() {
|
||||||
return input;
|
return outDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getOutDir() {
|
public String getOutDirSrc() {
|
||||||
return outputDir;
|
return outDirSrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOutputDir(File outputDir) {
|
public String getOutDirRes() {
|
||||||
this.outputDir = outputDir;
|
return outDirRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPrintHelp() {
|
public String getSingleClass() {
|
||||||
return printHelp;
|
return singleClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSingleClassOutput() {
|
||||||
|
return singleClassOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSkipResources() {
|
||||||
|
return skipResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSkipSources() {
|
||||||
|
return skipSources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getThreadsCount() {
|
public int getThreadsCount() {
|
||||||
return threadsCount;
|
return threadsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCFGOutput() {
|
|
||||||
return cfgOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRawCFGOutput() {
|
|
||||||
return rawCfgOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFallbackMode() {
|
public boolean isFallbackMode() {
|
||||||
return fallbackMode;
|
return fallbackMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isUseDx() {
|
||||||
public boolean isVerbose() {
|
return useDx;
|
||||||
return verbose;
|
}
|
||||||
|
|
||||||
|
public DecompilationMode getDecompilationMode() {
|
||||||
|
return decompilationMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShowInconsistentCode() {
|
||||||
|
return showInconsistentCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseImports() {
|
||||||
|
return useImports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDebugInfo() {
|
||||||
|
return debugInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAddDebugLines() {
|
||||||
|
return addDebugLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInlineAnonymousClasses() {
|
||||||
|
return inlineAnonymousClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInlineMethods() {
|
||||||
|
return inlineMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMoveInnerClasses() {
|
||||||
|
return moveInnerClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllowInlineKotlinLambda() {
|
||||||
|
return allowInlineKotlinLambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExtractFinally() {
|
||||||
|
return extractFinally;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getUserRenamesMappingsPath() {
|
||||||
|
return userRenamesMappingsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRenamesMappingsMode getUserRenamesMappingsMode() {
|
||||||
|
return userRenamesMappingsMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeobfuscationOn() {
|
||||||
|
return deobfuscationOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeobfuscationMinLength() {
|
||||||
|
return deobfuscationMinLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeobfuscationMaxLength() {
|
||||||
|
return deobfuscationMaxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeobfuscationWhitelistStr() {
|
||||||
|
return deobfuscationWhitelistStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGeneratedRenamesMappingFile() {
|
||||||
|
return generatedRenamesMappingFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {
|
||||||
|
return generatedRenamesMappingFileMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeobfuscationUseSourceNameAsAlias() {
|
||||||
|
return deobfuscationUseSourceNameAsAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceNameSource getResourceNameSource() {
|
||||||
|
return resourceNameSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() {
|
||||||
|
return useKotlinMethodsForVarNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegerFormat getIntegerFormat() {
|
||||||
|
return integerFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEscapeUnicode() {
|
||||||
|
return escapeUnicode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCfgOutput() {
|
||||||
|
return cfgOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRawCfgOutput() {
|
||||||
|
return rawCfgOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReplaceConsts() {
|
||||||
|
return replaceConsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRespectBytecodeAccessModifiers() {
|
||||||
|
return respectBytecodeAccessModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExportAsGradleProject() {
|
||||||
|
return exportAsGradleProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSkipXmlPrettyPrint() {
|
||||||
|
return skipXmlPrettyPrint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRenameCaseSensitive() {
|
||||||
|
return renameFlags.contains(RenameEnum.CASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRenameValid() {
|
||||||
|
return renameFlags.contains(RenameEnum.VALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRenamePrintable() {
|
||||||
|
return renameFlags.contains(RenameEnum.PRINTABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFsCaseSensitive() {
|
||||||
|
return fsCaseSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommentsLevel getCommentsLevel() {
|
||||||
|
return commentsLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogHelper.LogLevelEnum getLogLevel() {
|
||||||
|
return logLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getPluginOptions() {
|
||||||
|
return pluginOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RenameConverter implements IStringConverter<Set<RenameEnum>> {
|
||||||
|
private final String paramName;
|
||||||
|
|
||||||
|
RenameConverter(String paramName) {
|
||||||
|
this.paramName = paramName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RenameEnum> convert(String value) {
|
||||||
|
if (value.equalsIgnoreCase("NONE")) {
|
||||||
|
return EnumSet.noneOf(RenameEnum.class);
|
||||||
|
}
|
||||||
|
if (value.equalsIgnoreCase("ALL")) {
|
||||||
|
return EnumSet.allOf(RenameEnum.class);
|
||||||
|
}
|
||||||
|
Set<RenameEnum> set = EnumSet.noneOf(RenameEnum.class);
|
||||||
|
for (String s : value.split(",")) {
|
||||||
|
try {
|
||||||
|
set.add(RenameEnum.valueOf(s.trim().toUpperCase(Locale.ROOT)));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
'\'' + s + "' is unknown for parameter " + paramName
|
||||||
|
+ ", possible values are " + enumValuesString(RenameEnum.values()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CommentsLevelConverter extends BaseEnumConverter<CommentsLevel> {
|
||||||
|
public CommentsLevelConverter() {
|
||||||
|
super(CommentsLevel::valueOf, CommentsLevel::values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UseKotlinMethodsForVarNamesConverter extends BaseEnumConverter<UseKotlinMethodsForVarNames> {
|
||||||
|
public UseKotlinMethodsForVarNamesConverter() {
|
||||||
|
super(UseKotlinMethodsForVarNames::valueOf, UseKotlinMethodsForVarNames::values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DeobfuscationMapFileModeConverter extends BaseEnumConverter<GeneratedRenamesMappingFileMode> {
|
||||||
|
public DeobfuscationMapFileModeConverter() {
|
||||||
|
super(GeneratedRenamesMappingFileMode::valueOf, GeneratedRenamesMappingFileMode::values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ResourceNameSourceConverter extends BaseEnumConverter<ResourceNameSource> {
|
||||||
|
public ResourceNameSourceConverter() {
|
||||||
|
super(ResourceNameSource::valueOf, ResourceNameSource::values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DecompilationModeConverter extends BaseEnumConverter<DecompilationMode> {
|
||||||
|
public DecompilationModeConverter() {
|
||||||
|
super(DecompilationMode::valueOf, DecompilationMode::values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LogLevelConverter extends BaseEnumConverter<LogHelper.LogLevelEnum> {
|
||||||
|
public LogLevelConverter() {
|
||||||
|
super(LogHelper.LogLevelEnum::valueOf, LogHelper.LogLevelEnum::values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IntegerFormatConverter extends BaseEnumConverter<IntegerFormat> {
|
||||||
|
public IntegerFormatConverter() {
|
||||||
|
super(IntegerFormat::valueOf, IntegerFormat::values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class BaseEnumConverter<E extends Enum<E>> implements IStringConverter<E> {
|
||||||
|
private final Function<String, E> parse;
|
||||||
|
private final Supplier<E[]> values;
|
||||||
|
|
||||||
|
public BaseEnumConverter(Function<String, E> parse, Supplier<E[]> values) {
|
||||||
|
this.parse = parse;
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E convert(String value) {
|
||||||
|
try {
|
||||||
|
return parse.apply(stringAsEnumName(value));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
'\'' + value + "' is unknown, possible values are: " + enumValuesString(values.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String enumValuesString(Enum<?>[] values) {
|
||||||
|
return Stream.of(values)
|
||||||
|
.map(v -> v.name().replace('_', '-').toLowerCase(Locale.ROOT))
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stringAsEnumName(String value) {
|
||||||
|
// inverse of enumValuesString conversion
|
||||||
|
return value.replace('-', '_').toUpperCase(Locale.ROOT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package jadx.cli;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
|
||||||
|
import jadx.cli.commands.CommandPlugins;
|
||||||
|
import jadx.cli.commands.ICommand;
|
||||||
|
|
||||||
|
public class JadxCLICommands {
|
||||||
|
private static final Map<String, ICommand> COMMANDS_MAP = new TreeMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
JadxCLICommands.register(new CommandPlugins());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register(ICommand command) {
|
||||||
|
COMMANDS_MAP.put(command.name(), command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void append(JCommander.Builder builder) {
|
||||||
|
COMMANDS_MAP.forEach(builder::addCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean process(JCommanderWrapper<?> jcw, JCommander jc, String parsedCommand) {
|
||||||
|
ICommand command = COMMANDS_MAP.get(parsedCommand);
|
||||||
|
if (command == null) {
|
||||||
|
throw new IllegalArgumentException("Unknown command: " + parsedCommand);
|
||||||
|
}
|
||||||
|
JCommander subCommander = jc.getCommands().get(parsedCommand);
|
||||||
|
command.process(jcw, subCommander);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package jadx.cli;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
import ch.qos.logback.classic.Logger;
|
||||||
|
|
||||||
|
import jadx.api.JadxDecompiler;
|
||||||
|
|
||||||
|
public class LogHelper {
|
||||||
|
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(LogHelper.class);
|
||||||
|
|
||||||
|
public enum LogLevelEnum {
|
||||||
|
QUIET(Level.OFF),
|
||||||
|
PROGRESS(Level.OFF),
|
||||||
|
ERROR(Level.ERROR),
|
||||||
|
WARN(Level.WARN),
|
||||||
|
INFO(Level.INFO),
|
||||||
|
DEBUG(Level.DEBUG);
|
||||||
|
|
||||||
|
private final Level level;
|
||||||
|
|
||||||
|
LogLevelEnum(Level level) {
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Level getLevel() {
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable("For disable log level control")
|
||||||
|
private static LogLevelEnum logLevelValue;
|
||||||
|
|
||||||
|
public static void initLogLevel(JadxCLIArgs args) {
|
||||||
|
logLevelValue = getLogLevelFromArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogLevelEnum getLogLevelFromArgs(JadxCLIArgs args) {
|
||||||
|
if (isCustomLogConfig()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (args.quiet) {
|
||||||
|
return LogLevelEnum.QUIET;
|
||||||
|
}
|
||||||
|
if (args.verbose) {
|
||||||
|
return LogLevelEnum.DEBUG;
|
||||||
|
}
|
||||||
|
return args.logLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setLogLevel(LogLevelEnum newLogLevel) {
|
||||||
|
logLevelValue = newLogLevel;
|
||||||
|
applyLogLevel(logLevelValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setLogLevelsForLoadingStage() {
|
||||||
|
if (logLevelValue == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (logLevelValue == LogLevelEnum.PROGRESS) {
|
||||||
|
// show load errors
|
||||||
|
LogHelper.applyLogLevel(LogLevelEnum.ERROR);
|
||||||
|
fixForShowProgress();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applyLogLevel(logLevelValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setLogLevelsForDecompileStage() {
|
||||||
|
if (logLevelValue == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applyLogLevel(logLevelValue);
|
||||||
|
if (logLevelValue == LogLevelEnum.PROGRESS) {
|
||||||
|
fixForShowProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show progress: change to 'INFO' for control classes
|
||||||
|
*/
|
||||||
|
private static void fixForShowProgress() {
|
||||||
|
setLevelForClass(JadxCLI.class, Level.INFO);
|
||||||
|
setLevelForClass(JadxDecompiler.class, Level.INFO);
|
||||||
|
setLevelForClass(SingleClassMode.class, Level.INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyLogLevel(@NotNull LogLevelEnum logLevel) {
|
||||||
|
Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||||
|
rootLogger.setLevel(logLevel.getLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static LogLevelEnum getLogLevel() {
|
||||||
|
return logLevelValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setLevelForClass(Class<?> cls, Level level) {
|
||||||
|
((Logger) LoggerFactory.getLogger(cls)).setLevel(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setLevelForPackage(String pkgName, Level level) {
|
||||||
|
((Logger) LoggerFactory.getLogger(pkgName)).setLevel(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to detect if user provide custom logback config via -Dlogback.configurationFile=
|
||||||
|
*/
|
||||||
|
private static boolean isCustomLogConfig() {
|
||||||
|
try {
|
||||||
|
String logbackConfig = System.getProperty("logback.configurationFile");
|
||||||
|
if (logbackConfig == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG.debug("Use custom log config: {}", logbackConfig);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Failed to detect custom log config", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package jadx.cli;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.ICodeInfo;
|
||||||
|
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.JadxRuntimeException;
|
||||||
|
import jadx.core.utils.files.FileUtils;
|
||||||
|
|
||||||
|
public class SingleClassMode {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(SingleClassMode.class);
|
||||||
|
|
||||||
|
public static boolean process(JadxDecompiler jadx, JadxCLIArgs cliArgs) {
|
||||||
|
String singleClass = cliArgs.getSingleClass();
|
||||||
|
String singleClassOutput = cliArgs.getSingleClassOutput();
|
||||||
|
if (singleClass == null && singleClassOutput == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ClassNode clsForProcess;
|
||||||
|
if (singleClass != null) {
|
||||||
|
clsForProcess = jadx.getRoot().resolveClass(singleClass);
|
||||||
|
if (clsForProcess == null) {
|
||||||
|
clsForProcess = jadx.getRoot().getClasses().stream()
|
||||||
|
.filter(cls -> cls.getClassInfo().getAliasFullName().equals(singleClass))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
if (clsForProcess == null) {
|
||||||
|
throw new JadxRuntimeException("Input class not found: " + singleClass);
|
||||||
|
}
|
||||||
|
if (clsForProcess.contains(AFlag.DONT_GENERATE)) {
|
||||||
|
throw new JadxRuntimeException("Input class can't be saved by current jadx settings (marked as DONT_GENERATE)");
|
||||||
|
}
|
||||||
|
if (clsForProcess.isInner()) {
|
||||||
|
clsForProcess = clsForProcess.getTopParentClass();
|
||||||
|
LOG.warn("Input class is inner, parent class will be saved: {}", clsForProcess.getFullName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// singleClassOutput is set
|
||||||
|
// expect only one class to be loaded
|
||||||
|
List<ClassNode> classes = jadx.getRoot().getClasses().stream()
|
||||||
|
.filter(c -> !c.isInner() && !c.contains(AFlag.DONT_GENERATE))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
int size = classes.size();
|
||||||
|
if (size == 1) {
|
||||||
|
clsForProcess = classes.get(0);
|
||||||
|
} else {
|
||||||
|
throw new JadxRuntimeException("Found " + size + " classes, single class output can't be used");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ICodeInfo codeInfo;
|
||||||
|
try {
|
||||||
|
codeInfo = clsForProcess.decompile();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new JadxRuntimeException("Class decompilation failed", e);
|
||||||
|
}
|
||||||
|
String fileExt = SaveCode.getFileExtension(jadx.getRoot());
|
||||||
|
File out;
|
||||||
|
if (singleClassOutput == null) {
|
||||||
|
out = new File(jadx.getArgs().getOutDirSrc(), clsForProcess.getClassInfo().getAliasFullPath() + fileExt);
|
||||||
|
} else {
|
||||||
|
if (singleClassOutput.endsWith(fileExt)) {
|
||||||
|
// treat as file name
|
||||||
|
out = new File(singleClassOutput);
|
||||||
|
} else {
|
||||||
|
// treat as directory
|
||||||
|
out = new File(singleClassOutput, clsForProcess.getShortName() + fileExt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File resultOut = FileUtils.prepareFile(out);
|
||||||
|
if (clsForProcess.getClassInfo().hasAlias()) {
|
||||||
|
LOG.info("Saving class '{}' (alias: '{}') to file '{}'",
|
||||||
|
clsForProcess.getClassInfo().getFullName(), clsForProcess.getFullName(), resultOut.getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
LOG.info("Saving class '{}' to file '{}'", clsForProcess.getFullName(), resultOut.getAbsolutePath());
|
||||||
|
}
|
||||||
|
SaveCode.save(codeInfo.getCodeStr(), resultOut);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package jadx.cli.clst;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.JadxArgs;
|
||||||
|
import jadx.api.JadxDecompiler;
|
||||||
|
import jadx.core.clsp.ClsSet;
|
||||||
|
import jadx.core.dex.nodes.RootNode;
|
||||||
|
import jadx.core.utils.files.FileUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for convert dex or jar to jadx classes set (.jcst)
|
||||||
|
*/
|
||||||
|
public class ConvertToClsSet {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ConvertToClsSet.class);
|
||||||
|
|
||||||
|
public static void usage() {
|
||||||
|
LOG.info("<output .jcst file> <several input dex or jar files> ");
|
||||||
|
LOG.info("Arguments to update core.jcst: "
|
||||||
|
+ "<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) {
|
||||||
|
if (args.length < 2) {
|
||||||
|
usage();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
List<Path> inputPaths = Stream.of(args).map(Paths::get).collect(Collectors.toList());
|
||||||
|
Path output = inputPaths.remove(0);
|
||||||
|
|
||||||
|
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));
|
||||||
|
jadxArgs.setUseSourceNameAsClassAlias(false);
|
||||||
|
jadxArgs.setMoveInnerClasses(false);
|
||||||
|
jadxArgs.setInlineAnonymousClasses(false);
|
||||||
|
jadxArgs.setInlineMethods(false);
|
||||||
|
|
||||||
|
// 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.loadFrom(root);
|
||||||
|
set.save(output);
|
||||||
|
|
||||||
|
LOG.info("Output: {}", output);
|
||||||
|
LOG.info("done");
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Failed with error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package jadx.cli.commands;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
|
||||||
|
import jadx.cli.JCommanderWrapper;
|
||||||
|
import jadx.plugins.tools.JadxPluginsList;
|
||||||
|
import jadx.plugins.tools.JadxPluginsTools;
|
||||||
|
import jadx.plugins.tools.data.JadxPluginMetadata;
|
||||||
|
import jadx.plugins.tools.data.JadxPluginUpdate;
|
||||||
|
|
||||||
|
@Parameters(commandDescription = "manage jadx plugins")
|
||||||
|
public class CommandPlugins implements ICommand {
|
||||||
|
|
||||||
|
@Parameter(names = { "-i", "--install" }, description = "install plugin with locationId")
|
||||||
|
protected String install;
|
||||||
|
|
||||||
|
@Parameter(names = { "-j", "--install-jar" }, description = "install plugin from jar file")
|
||||||
|
protected String installJar;
|
||||||
|
|
||||||
|
@Parameter(names = { "-l", "--list" }, description = "list installed plugins")
|
||||||
|
protected boolean list;
|
||||||
|
|
||||||
|
@Parameter(names = { "-a", "--available" }, description = "list available plugins")
|
||||||
|
protected boolean available;
|
||||||
|
|
||||||
|
@Parameter(names = { "-u", "--update" }, description = "update installed plugins")
|
||||||
|
protected boolean update;
|
||||||
|
|
||||||
|
@Parameter(names = { "--uninstall" }, description = "uninstall plugin with pluginId")
|
||||||
|
protected String uninstall;
|
||||||
|
|
||||||
|
@Parameter(names = { "-h", "--help" }, description = "print this help", help = true)
|
||||||
|
protected boolean printHelp = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return "plugins";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(JCommanderWrapper<?> jcw, JCommander subCommander) {
|
||||||
|
if (printHelp) {
|
||||||
|
jcw.printUsage(subCommander);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (install != null) {
|
||||||
|
installPlugin(install);
|
||||||
|
}
|
||||||
|
if (installJar != null) {
|
||||||
|
installPlugin("file:" + installJar);
|
||||||
|
}
|
||||||
|
if (uninstall != null) {
|
||||||
|
boolean uninstalled = JadxPluginsTools.getInstance().uninstall(uninstall);
|
||||||
|
System.out.println(uninstalled ? "Uninstalled" : "Plugin not found");
|
||||||
|
}
|
||||||
|
if (update) {
|
||||||
|
List<JadxPluginUpdate> updates = JadxPluginsTools.getInstance().updateAll();
|
||||||
|
if (updates.isEmpty()) {
|
||||||
|
System.out.println("No updates");
|
||||||
|
} else {
|
||||||
|
System.out.println("Installed updates: " + updates.size());
|
||||||
|
for (JadxPluginUpdate update : updates) {
|
||||||
|
System.out.println(" " + update.getPluginId() + ": " + update.getOldVersion() + " -> " + update.getNewVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (list) {
|
||||||
|
List<JadxPluginMetadata> installed = JadxPluginsTools.getInstance().getInstalled();
|
||||||
|
System.out.println("Installed plugins: " + installed.size());
|
||||||
|
int i = 1;
|
||||||
|
for (JadxPluginMetadata plugin : installed) {
|
||||||
|
System.out.println(" " + (i++) + ") "
|
||||||
|
+ plugin.getPluginId() + " (" + plugin.getVersion() + ") - "
|
||||||
|
+ plugin.getName() + ": " + plugin.getDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (available) {
|
||||||
|
List<JadxPluginMetadata> availableList = JadxPluginsList.getInstance().get();
|
||||||
|
System.out.println("Available plugins: " + availableList.size());
|
||||||
|
int i = 1;
|
||||||
|
for (JadxPluginMetadata plugin : availableList) {
|
||||||
|
System.out.println(" " + (i++) + ") "
|
||||||
|
+ plugin.getName() + ": " + plugin.getDescription()
|
||||||
|
+ " (" + plugin.getLocationId() + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installPlugin(String locationId) {
|
||||||
|
JadxPluginMetadata plugin = JadxPluginsTools.getInstance().install(locationId);
|
||||||
|
System.out.println("Plugin installed: " + plugin.getPluginId() + ":" + plugin.getVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package jadx.cli.commands;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
|
||||||
|
import jadx.cli.JCommanderWrapper;
|
||||||
|
|
||||||
|
public interface ICommand {
|
||||||
|
String name();
|
||||||
|
|
||||||
|
void process(JCommanderWrapper<?> jcw, JCommander subCommander);
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package jadx.cli.tools;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.HashMap;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import jadx.api.JadxArgs;
|
||||||
|
import jadx.core.dex.nodes.RootNode;
|
||||||
|
import jadx.core.utils.android.TextResMapFile;
|
||||||
|
import jadx.core.xmlgen.ResTableParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for convert '.arsc' to simple text file with mapping id to resource name
|
||||||
|
*/
|
||||||
|
public class ConvertArscFile {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ConvertArscFile.class);
|
||||||
|
private static int rewritesCount;
|
||||||
|
|
||||||
|
public static void usage() {
|
||||||
|
LOG.info("<res-map file> <input .arsc files>");
|
||||||
|
LOG.info("");
|
||||||
|
LOG.info("Note: If res-map already exists - it will be merged and updated");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
if (args.length < 2) {
|
||||||
|
usage();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
List<Path> inputPaths = Stream.of(args).map(Paths::get).collect(Collectors.toList());
|
||||||
|
Path resMapFile = inputPaths.remove(0);
|
||||||
|
Map<Integer, String> resMap;
|
||||||
|
if (Files.isReadable(resMapFile)) {
|
||||||
|
resMap = TextResMapFile.read(resMapFile);
|
||||||
|
} else {
|
||||||
|
resMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
LOG.info("Input entries count: {}", resMap.size());
|
||||||
|
|
||||||
|
RootNode root = new RootNode(new JadxArgs()); // not really needed
|
||||||
|
rewritesCount = 0;
|
||||||
|
for (Path resFile : inputPaths) {
|
||||||
|
LOG.info("Processing {}", resFile);
|
||||||
|
ResTableParser resTableParser = new ResTableParser(root, true);
|
||||||
|
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");
|
||||||
|
if (entry == null) {
|
||||||
|
LOG.error("Failed to load \"resources.arsc\" from {}", resFile);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try (InputStream inputStream = zip.getInputStream(entry)) {
|
||||||
|
resTableParser.decode(inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Load resources.arsc from extracted file
|
||||||
|
try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(resFile))) {
|
||||||
|
resTableParser.decode(inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<Integer, String> singleResMap = resTableParser.getResStorage().getResourcesNames();
|
||||||
|
mergeResMaps(resMap, singleResMap);
|
||||||
|
LOG.info("{} entries count: {}, after merge: {}", resFile.getFileName(), singleResMap.size(), resMap.size());
|
||||||
|
}
|
||||||
|
LOG.info("Output entries count: {}", resMap.size());
|
||||||
|
LOG.info("Total rewrites count: {}", rewritesCount);
|
||||||
|
TextResMapFile.write(resMapFile, resMap);
|
||||||
|
LOG.info("Result file size: {} B", resMapFile.toFile().length());
|
||||||
|
LOG.info("done");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void mergeResMaps(Map<Integer, String> mainResMap, Map<Integer, String> newResMap) {
|
||||||
|
for (Map.Entry<Integer, String> entry : newResMap.entrySet()) {
|
||||||
|
Integer id = entry.getKey();
|
||||||
|
String name = entry.getValue();
|
||||||
|
String prevName = mainResMap.put(id, name);
|
||||||
|
if (prevName != null && !name.equals(prevName)) {
|
||||||
|
LOG.debug("Rewrite id: {} from: '{}' to: '{}'", Integer.toHexString(id), prevName, name);
|
||||||
|
rewritesCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
|
<!-- Jadx logger config. Used both in cli and gui -->
|
||||||
|
|
||||||
<configuration>
|
<configuration>
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%-5level - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<!-- jadx-gui -->
|
||||||
<encoder>
|
<logger name="com.pinterest.ktlint" level="INFO"/>
|
||||||
<pattern>%-5level - %msg%n</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<root level="INFO">
|
|
||||||
<appender-ref ref="STDOUT"/>
|
|
||||||
</root>
|
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</root>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package jadx.cli;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static jadx.core.utils.Utils.newConstStringMap;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
public class JadxCLIArgsTest {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JadxCLIArgsTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvertedBooleanOption() {
|
||||||
|
assertThat(parse("--no-replace-consts").isReplaceConsts(), is(false));
|
||||||
|
assertThat(parse("").isReplaceConsts(), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEscapeUnicodeOption() {
|
||||||
|
assertThat(parse("--escape-unicode").isEscapeUnicode(), is(true));
|
||||||
|
assertThat(parse("").isEscapeUnicode(), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSrcOption() {
|
||||||
|
assertThat(parse("--no-src").isSkipSources(), is(true));
|
||||||
|
assertThat(parse("-s").isSkipSources(), is(true));
|
||||||
|
assertThat(parse("").isSkipSources(), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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));
|
||||||
|
|
||||||
|
JadxCLIArgs args = new JadxCLIArgs();
|
||||||
|
args.useImports = false;
|
||||||
|
assertThat(override(args, "--no-imports").isUseImports(), is(false));
|
||||||
|
args.debugInfo = false;
|
||||||
|
assertThat(override(args, "--no-debug-info").isDebugInfo(), is(false));
|
||||||
|
|
||||||
|
args = new JadxCLIArgs();
|
||||||
|
args.useImports = false;
|
||||||
|
assertThat(override(args, "").isUseImports(), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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, Matchers.equalTo(expectedMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JadxCLIArgs parse(String... args) {
|
||||||
|
return parse(new JadxCLIArgs(), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JadxCLIArgs parse(JadxCLIArgs jadxArgs, String... args) {
|
||||||
|
boolean res = jadxArgs.processArgs(args);
|
||||||
|
assertThat(res, is(true));
|
||||||
|
LOG.info("Jadx args: {}", jadxArgs.toJadxArgs());
|
||||||
|
return jadxArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JadxCLIArgs override(JadxCLIArgs jadxArgs, String... args) {
|
||||||
|
boolean res = jadxArgs.overrideProvided(args);
|
||||||
|
assertThat(res, is(true));
|
||||||
|
LOG.info("Jadx args: {}", jadxArgs.toJadxArgs());
|
||||||
|
return jadxArgs;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package jadx.cli;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jadx.api.JadxArgs.RenameEnum;
|
||||||
|
import jadx.cli.JadxCLIArgs.RenameConverter;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class RenameConverterTest {
|
||||||
|
|
||||||
|
private RenameConverter converter;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void init() {
|
||||||
|
converter = new RenameConverter("someParam");
|
||||||
|
}
|
||||||
|
|
||||||
|
@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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void none() {
|
||||||
|
Set<RenameEnum> set = converter.convert("none");
|
||||||
|
assertTrue(set.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void wrong() {
|
||||||
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
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.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);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDexInput() throws Exception {
|
||||||
|
decompile("dex", "samples/hello.dex");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSmaliInput() throws Exception {
|
||||||
|
decompile("smali", "samples/HelloWorld.smali");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassInput() throws Exception {
|
||||||
|
decompile("class", "samples/HelloWorld.class");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleInput() throws Exception {
|
||||||
|
decompile("multi", "samples/hello.dex", "samples/HelloWorld.smali");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResourceOnly() throws Exception {
|
||||||
|
decode("resourceOnly", "samples/resources-only.apk");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decode(String tmpDirName, String apkSample) throws URISyntaxException, IOException {
|
||||||
|
List<String> args = new ArrayList<>();
|
||||||
|
Path tempDir = FileUtils.createTempDir(tmpDirName);
|
||||||
|
args.add("-v");
|
||||||
|
args.add("-d");
|
||||||
|
args.add(tempDir.toAbsolutePath().toString());
|
||||||
|
|
||||||
|
URL resource = getClass().getClassLoader().getResource(apkSample);
|
||||||
|
assertThat(resource).isNotNull();
|
||||||
|
String sampleFile = resource.toURI().getRawPath();
|
||||||
|
args.add(sampleFile);
|
||||||
|
|
||||||
|
int result = JadxCLI.execute(args.toArray(new String[0]));
|
||||||
|
assertThat(result).isEqualTo(0);
|
||||||
|
List<Path> files = Files.find(
|
||||||
|
tempDir,
|
||||||
|
3,
|
||||||
|
(file, attr) -> file.getFileName().toString().equalsIgnoreCase("AndroidManifest.xml"))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(files.isEmpty()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decompile(String tmpDirName, String... inputSamples) throws URISyntaxException, IOException {
|
||||||
|
List<String> args = new ArrayList<>();
|
||||||
|
Path tempDir = FileUtils.createTempDir(tmpDirName);
|
||||||
|
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]));
|
||||||
|
assertThat(result).isEqualTo(0);
|
||||||
|
List<Path> resultJavaFiles = collectJavaFilesInDir(tempDir);
|
||||||
|
assertThat(resultJavaFiles).isNotEmpty();
|
||||||
|
|
||||||
|
// do not copy input files as resources
|
||||||
|
PathMatcher logAllFiles = path -> {
|
||||||
|
LOG.debug("File in result dir: {}", path);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
for (Path path : collectFilesInDir(tempDir, logAllFiles)) {
|
||||||
|
for (String inputSample : inputSamples) {
|
||||||
|
assertThat(path.toAbsolutePath().toString()).doesNotContain(inputSample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Path> collectJavaFilesInDir(Path dir) throws IOException {
|
||||||
|
PathMatcher javaMatcher = dir.getFileSystem().getPathMatcher("glob:**.java");
|
||||||
|
return collectFilesInDir(dir, javaMatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
public static void cleanup() {
|
||||||
|
FileUtils.clearTempRootDir();
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,26 @@
|
|||||||
|
.class Lsmali/HelloWorld;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.source "HelloWorld.java"
|
||||||
|
|
||||||
|
.method constructor <init>()V
|
||||||
|
.registers 1
|
||||||
|
|
||||||
|
.line 1
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static main([Ljava/lang/String;)V
|
||||||
|
.registers 2
|
||||||
|
|
||||||
|
.line 3
|
||||||
|
sget-object p0, Ljava/lang/System;->out:Ljava/io/PrintStream;
|
||||||
|
|
||||||
|
const-string v0, "Hello, World"
|
||||||
|
|
||||||
|
invoke-virtual {p0, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||||
|
|
||||||
|
.line 4
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,9 +0,0 @@
|
|||||||
ext.jadxClasspath = 'clsp-data/android-4.3.jar'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile 'com.google.android.tools:dx:1.7'
|
|
||||||
compile 'ch.qos.logback:logback-classic:1.0.13'
|
|
||||||
|
|
||||||
runtime files(jadxClasspath)
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
plugins {
|
||||||
|
id("jadx-library")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(":jadx-plugins:jadx-input-api"))
|
||||||
|
|
||||||
|
implementation("com.google.code.gson:gson:2.10.1")
|
||||||
|
|
||||||
|
// TODO: move resources decoding to separate plugin module
|
||||||
|
implementation("com.android.tools.build:aapt2-proto:8.2.2-10154469")
|
||||||
|
implementation("com.google.protobuf:protobuf-java:3.25.2") // forcing latest version
|
||||||
|
|
||||||
|
testImplementation("org.apache.commons:commons-lang3:3.14.0")
|
||||||
|
|
||||||
|
testImplementation(project(":jadx-plugins:jadx-dex-input"))
|
||||||
|
testRuntimeOnly(project(":jadx-plugins:jadx-smali-input"))
|
||||||
|
testRuntimeOnly(project(":jadx-plugins:jadx-java-convert"))
|
||||||
|
testRuntimeOnly(project(":jadx-plugins:jadx-java-input"))
|
||||||
|
testRuntimeOnly(project(":jadx-plugins:jadx-raung-input"))
|
||||||
|
|
||||||
|
testImplementation("org.eclipse.jdt:ecj") {
|
||||||
|
version {
|
||||||
|
prefer("3.33.0")
|
||||||
|
strictly("[3.33, 3.34[") // from 3.34 compiled with Java 17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testImplementation("tools.profiler:async-profiler:3.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
exclude("**/tmp/*")
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,14 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
public enum CommentsLevel {
|
||||||
|
NONE,
|
||||||
|
USER_ONLY,
|
||||||
|
ERROR,
|
||||||
|
WARN,
|
||||||
|
INFO,
|
||||||
|
DEBUG;
|
||||||
|
|
||||||
|
public boolean filter(CommentsLevel limit) {
|
||||||
|
return this.ordinal() <= limit.ordinal();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
public enum DecompilationMode {
|
||||||
|
/**
|
||||||
|
* Trying best options (default)
|
||||||
|
*/
|
||||||
|
AUTO,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore code structure (normal java code)
|
||||||
|
*/
|
||||||
|
RESTRUCTURE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplified instructions (linear with goto's)
|
||||||
|
*/
|
||||||
|
SIMPLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw instructions without modifications
|
||||||
|
*/
|
||||||
|
FALLBACK
|
||||||
|
}
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
package jadx.api;
|
|
||||||
|
|
||||||
import jadx.core.Jadx;
|
|
||||||
import jadx.core.ProcessClass;
|
|
||||||
import jadx.core.dex.info.ClassInfo;
|
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
|
||||||
import jadx.core.dex.nodes.RootNode;
|
|
||||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
|
||||||
import jadx.core.dex.visitors.SaveCode;
|
|
||||||
import jadx.core.utils.ErrorsCounter;
|
|
||||||
import jadx.core.utils.exceptions.CodegenException;
|
|
||||||
import jadx.core.utils.exceptions.DecodeException;
|
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
|
||||||
import jadx.core.utils.files.InputFile;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Jadx API usage example:
|
|
||||||
* <pre><code>
|
|
||||||
* Decompiler jadx = new Decompiler();
|
|
||||||
* jadx.loadFile(new File("classes.dex"));
|
|
||||||
* jadx.setOutputDir(new File("out"));
|
|
||||||
* jadx.save();
|
|
||||||
* </code></pre>
|
|
||||||
* <p/>
|
|
||||||
* Instead of 'save()' you can get list of decompiled classes:
|
|
||||||
* <pre><code>
|
|
||||||
* for(JavaClass cls : jadx.getClasses()) {
|
|
||||||
* System.out.println(cls.getCode());
|
|
||||||
* }
|
|
||||||
* </code></pre>
|
|
||||||
*/
|
|
||||||
public final class Decompiler {
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Decompiler.class);
|
|
||||||
|
|
||||||
private final IJadxArgs args;
|
|
||||||
private final List<InputFile> inputFiles = new ArrayList<InputFile>();
|
|
||||||
|
|
||||||
private File outDir;
|
|
||||||
|
|
||||||
private RootNode root;
|
|
||||||
private List<IDexTreeVisitor> passes;
|
|
||||||
|
|
||||||
public Decompiler() {
|
|
||||||
this.args = new DefaultJadxArgs();
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Decompiler(IJadxArgs jadxArgs) {
|
|
||||||
this.args = jadxArgs;
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOutputDir(File outDir) {
|
|
||||||
this.outDir = outDir;
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void init() {
|
|
||||||
if (outDir == null) {
|
|
||||||
outDir = new File("jadx-output");
|
|
||||||
}
|
|
||||||
this.passes = Jadx.getPassesList(args, outDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadFile(File file) throws IOException, DecodeException {
|
|
||||||
loadFiles(Arrays.asList(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadFiles(List<File> files) throws IOException, DecodeException {
|
|
||||||
if (files.isEmpty()) {
|
|
||||||
throw new JadxRuntimeException("Empty file list");
|
|
||||||
}
|
|
||||||
inputFiles.clear();
|
|
||||||
for (File file : files) {
|
|
||||||
inputFiles.add(new InputFile(file));
|
|
||||||
}
|
|
||||||
parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() {
|
|
||||||
try {
|
|
||||||
ExecutorService ex = getSaveExecutor();
|
|
||||||
ex.awaitTermination(1, TimeUnit.DAYS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.error("Save interrupted", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThreadPoolExecutor getSaveExecutor() {
|
|
||||||
if (root == null) {
|
|
||||||
throw new JadxRuntimeException("No loaded files");
|
|
||||||
}
|
|
||||||
int threadsCount = args.getThreadsCount();
|
|
||||||
LOG.debug("processing threads count: {}", threadsCount);
|
|
||||||
|
|
||||||
ArrayList<IDexTreeVisitor> passList = new ArrayList<IDexTreeVisitor>(passes);
|
|
||||||
SaveCode savePass = new SaveCode(outDir, args);
|
|
||||||
passList.add(savePass);
|
|
||||||
|
|
||||||
LOG.info("processing ...");
|
|
||||||
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
|
|
||||||
for (ClassNode cls : root.getClasses(false)) {
|
|
||||||
if (cls.getCode() == null) {
|
|
||||||
ProcessClass job = new ProcessClass(cls, passList);
|
|
||||||
executor.execute(job);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
savePass.visit(cls);
|
|
||||||
} catch (CodegenException e) {
|
|
||||||
LOG.error("Can't save class {}", cls, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
executor.shutdown();
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<JavaClass> getClasses() {
|
|
||||||
List<ClassNode> classNodeList = root.getClasses(false);
|
|
||||||
List<JavaClass> classes = new ArrayList<JavaClass>(classNodeList.size());
|
|
||||||
for (ClassNode classNode : classNodeList) {
|
|
||||||
classes.add(new JavaClass(this, classNode));
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<JavaPackage> getPackages() {
|
|
||||||
List<JavaClass> classes = getClasses();
|
|
||||||
Map<String, List<JavaClass>> map = new HashMap<String, List<JavaClass>>();
|
|
||||||
for (JavaClass javaClass : classes) {
|
|
||||||
String pkg = javaClass.getPackage();
|
|
||||||
List<JavaClass> clsList = map.get(pkg);
|
|
||||||
if (clsList == null) {
|
|
||||||
clsList = new ArrayList<JavaClass>();
|
|
||||||
map.put(pkg, clsList);
|
|
||||||
}
|
|
||||||
clsList.add(javaClass);
|
|
||||||
}
|
|
||||||
List<JavaPackage> packages = new ArrayList<JavaPackage>(map.size());
|
|
||||||
for (Map.Entry<String, List<JavaClass>> entry : map.entrySet()) {
|
|
||||||
packages.add(new JavaPackage(entry.getKey(), entry.getValue()));
|
|
||||||
}
|
|
||||||
Collections.sort(packages);
|
|
||||||
for (JavaPackage pkg : packages) {
|
|
||||||
Collections.sort(pkg.getClasses(), new Comparator<JavaClass>() {
|
|
||||||
@Override
|
|
||||||
public int compare(JavaClass o1, JavaClass o2) {
|
|
||||||
return o1.getShortName().compareTo(o2.getShortName());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(packages);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getErrorsCount() {
|
|
||||||
return ErrorsCounter.getErrorCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse() throws DecodeException {
|
|
||||||
ClassInfo.clearCache();
|
|
||||||
ErrorsCounter.reset();
|
|
||||||
|
|
||||||
root = new RootNode();
|
|
||||||
LOG.info("loading ...");
|
|
||||||
root.load(inputFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
void processClass(ClassNode cls) {
|
|
||||||
try {
|
|
||||||
ProcessClass job = new ProcessClass(cls, passes);
|
|
||||||
LOG.info("processing class {} ...", cls);
|
|
||||||
job.run();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LOG.error("Process class error", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RootNode getRoot() {
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package jadx.api;
|
|
||||||
|
|
||||||
public class DefaultJadxArgs implements IJadxArgs {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getThreadsCount() {
|
|
||||||
return Runtime.getRuntime().availableProcessors();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCFGOutput() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRawCFGOutput() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFallbackMode() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isVerbose() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface ICodeCache extends Closeable {
|
||||||
|
|
||||||
|
void add(String clsFullName, ICodeInfo codeInfo);
|
||||||
|
|
||||||
|
void remove(String clsFullName);
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
ICodeInfo get(String clsFullName);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
String getCode(String clsFullName);
|
||||||
|
|
||||||
|
boolean contains(String clsFullName);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import jadx.api.impl.SimpleCodeInfo;
|
||||||
|
import jadx.api.metadata.ICodeMetadata;
|
||||||
|
|
||||||
|
public interface ICodeInfo {
|
||||||
|
|
||||||
|
ICodeInfo EMPTY = new SimpleCodeInfo("");
|
||||||
|
|
||||||
|
String getCodeStr();
|
||||||
|
|
||||||
|
ICodeMetadata getCodeMetadata();
|
||||||
|
|
||||||
|
boolean hasMetadata();
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
|
|
||||||
|
public interface ICodeWriter {
|
||||||
|
String NL = System.getProperty("line.separator");
|
||||||
|
String INDENT_STR = " ";
|
||||||
|
|
||||||
|
boolean isMetadataSupported();
|
||||||
|
|
||||||
|
ICodeWriter startLine();
|
||||||
|
|
||||||
|
ICodeWriter startLine(char c);
|
||||||
|
|
||||||
|
ICodeWriter startLine(String str);
|
||||||
|
|
||||||
|
ICodeWriter startLineWithNum(int sourceLine);
|
||||||
|
|
||||||
|
ICodeWriter addMultiLine(String str);
|
||||||
|
|
||||||
|
ICodeWriter add(String str);
|
||||||
|
|
||||||
|
ICodeWriter add(char c);
|
||||||
|
|
||||||
|
ICodeWriter add(ICodeWriter code);
|
||||||
|
|
||||||
|
ICodeWriter newLine();
|
||||||
|
|
||||||
|
ICodeWriter addIndent();
|
||||||
|
|
||||||
|
void incIndent();
|
||||||
|
|
||||||
|
void decIndent();
|
||||||
|
|
||||||
|
int getIndent();
|
||||||
|
|
||||||
|
void setIndent(int indent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return current line (only if metadata is supported)
|
||||||
|
*/
|
||||||
|
int getLine();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return start line position (only if metadata is supported)
|
||||||
|
*/
|
||||||
|
int getLineStartPos();
|
||||||
|
|
||||||
|
void attachDefinition(ICodeNodeRef obj);
|
||||||
|
|
||||||
|
void attachAnnotation(ICodeAnnotation obj);
|
||||||
|
|
||||||
|
void attachLineAnnotation(ICodeAnnotation obj);
|
||||||
|
|
||||||
|
void attachSourceLine(int sourceLine);
|
||||||
|
|
||||||
|
ICodeInfo finish();
|
||||||
|
|
||||||
|
String getCodeStr();
|
||||||
|
|
||||||
|
int getLength();
|
||||||
|
|
||||||
|
StringBuilder getRawBuf();
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
Map<Integer, ICodeAnnotation> getRawAnnotations();
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IDecompileScheduler {
|
||||||
|
List<List<JavaClass>> buildBatches(List<JavaClass> classes);
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package jadx.api;
|
|
||||||
|
|
||||||
public interface IJadxArgs {
|
|
||||||
int getThreadsCount();
|
|
||||||
|
|
||||||
boolean isCFGOutput();
|
|
||||||
|
|
||||||
boolean isRawCFGOutput();
|
|
||||||
|
|
||||||
boolean isFallbackMode();
|
|
||||||
|
|
||||||
boolean isVerbose();
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,759 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.args.GeneratedRenamesMappingFileMode;
|
||||||
|
import jadx.api.args.IntegerFormat;
|
||||||
|
import jadx.api.args.ResourceNameSource;
|
||||||
|
import jadx.api.args.UserRenamesMappingsMode;
|
||||||
|
import jadx.api.data.ICodeData;
|
||||||
|
import jadx.api.deobf.IAliasProvider;
|
||||||
|
import jadx.api.deobf.IRenameCondition;
|
||||||
|
import jadx.api.impl.AnnotatedCodeWriter;
|
||||||
|
import jadx.api.impl.InMemoryCodeCache;
|
||||||
|
import jadx.api.plugins.loader.JadxBasePluginLoader;
|
||||||
|
import jadx.api.plugins.loader.JadxPluginLoader;
|
||||||
|
import jadx.api.usage.IUsageInfoCache;
|
||||||
|
import jadx.api.usage.impl.InMemoryUsageInfoCache;
|
||||||
|
import jadx.core.deobf.DeobfAliasProvider;
|
||||||
|
import jadx.core.deobf.conditions.DeobfWhitelist;
|
||||||
|
import jadx.core.deobf.conditions.JadxRenameConditions;
|
||||||
|
import jadx.core.plugins.PluginContext;
|
||||||
|
import jadx.core.utils.files.FileUtils;
|
||||||
|
|
||||||
|
public class JadxArgs implements Closeable {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JadxArgs.class);
|
||||||
|
|
||||||
|
public static final int DEFAULT_THREADS_COUNT = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
|
||||||
|
|
||||||
|
public static final String DEFAULT_OUT_DIR = "jadx-output";
|
||||||
|
public static final String DEFAULT_SRC_DIR = "sources";
|
||||||
|
public static final String DEFAULT_RES_DIR = "resources";
|
||||||
|
|
||||||
|
private List<File> inputFiles = new ArrayList<>(1);
|
||||||
|
|
||||||
|
private File outDir;
|
||||||
|
private File outDirSrc;
|
||||||
|
private File outDirRes;
|
||||||
|
|
||||||
|
private ICodeCache codeCache = new InMemoryCodeCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usage data cache. Saves use places of classes, methods and fields between code reloads.
|
||||||
|
* Can be set to {@link jadx.api.usage.impl.EmptyUsageInfoCache} if code reload not needed.
|
||||||
|
*/
|
||||||
|
private IUsageInfoCache usageInfoCache = new InMemoryUsageInfoCache();
|
||||||
|
|
||||||
|
private Function<JadxArgs, ICodeWriter> codeWriterProvider = AnnotatedCodeWriter::new;
|
||||||
|
|
||||||
|
private int threadsCount = DEFAULT_THREADS_COUNT;
|
||||||
|
|
||||||
|
private boolean cfgOutput = false;
|
||||||
|
private boolean rawCFGOutput = false;
|
||||||
|
|
||||||
|
private boolean showInconsistentCode = false;
|
||||||
|
|
||||||
|
private boolean useImports = true;
|
||||||
|
private boolean debugInfo = true;
|
||||||
|
private boolean insertDebugLines = false;
|
||||||
|
private boolean extractFinally = true;
|
||||||
|
private boolean inlineAnonymousClasses = true;
|
||||||
|
private boolean inlineMethods = true;
|
||||||
|
private boolean allowInlineKotlinLambda = true;
|
||||||
|
private boolean moveInnerClasses = true;
|
||||||
|
|
||||||
|
private boolean skipResources = false;
|
||||||
|
private boolean skipSources = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate that allows to filter the classes to be process based on their full name
|
||||||
|
*/
|
||||||
|
private Predicate<String> classFilter = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save dependencies for classes accepted by {@code classFilter}
|
||||||
|
*/
|
||||||
|
private boolean includeDependencies = false;
|
||||||
|
|
||||||
|
private Path userRenamesMappingsPath = null;
|
||||||
|
private UserRenamesMappingsMode userRenamesMappingsMode = UserRenamesMappingsMode.getDefault();
|
||||||
|
|
||||||
|
private boolean deobfuscationOn = false;
|
||||||
|
private boolean useSourceNameAsClassAlias = false;
|
||||||
|
|
||||||
|
private File generatedRenamesMappingFile = null;
|
||||||
|
private GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.getDefault();
|
||||||
|
private ResourceNameSource resourceNameSource = ResourceNameSource.AUTO;
|
||||||
|
|
||||||
|
private int deobfuscationMinLength = 0;
|
||||||
|
private int deobfuscationMaxLength = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of classes and packages (ends with '.*') to exclude from deobfuscation
|
||||||
|
*/
|
||||||
|
private List<String> deobfuscationWhitelist = DeobfWhitelist.DEFAULT_LIST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nodes alias provider for deobfuscator and rename visitor
|
||||||
|
*/
|
||||||
|
private IAliasProvider aliasProvider = new DeobfAliasProvider();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Condition to rename node in deobfuscator
|
||||||
|
*/
|
||||||
|
private IRenameCondition renameCondition = JadxRenameConditions.buildDefault();
|
||||||
|
|
||||||
|
private boolean escapeUnicode = false;
|
||||||
|
private boolean replaceConsts = true;
|
||||||
|
private boolean respectBytecodeAccModifiers = false;
|
||||||
|
private boolean exportAsGradleProject = false;
|
||||||
|
|
||||||
|
private boolean skipXmlPrettyPrint = false;
|
||||||
|
|
||||||
|
private boolean fsCaseSensitive;
|
||||||
|
|
||||||
|
public enum RenameEnum {
|
||||||
|
CASE, VALID, PRINTABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<RenameEnum> renameFlags = EnumSet.allOf(RenameEnum.class);
|
||||||
|
|
||||||
|
public enum OutputFormatEnum {
|
||||||
|
JAVA, JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputFormatEnum outputFormat = OutputFormatEnum.JAVA;
|
||||||
|
|
||||||
|
private DecompilationMode decompilationMode = DecompilationMode.AUTO;
|
||||||
|
|
||||||
|
private ICodeData codeData;
|
||||||
|
|
||||||
|
private CommentsLevel commentsLevel = CommentsLevel.INFO;
|
||||||
|
|
||||||
|
private IntegerFormat integerFormat = IntegerFormat.AUTO;
|
||||||
|
|
||||||
|
private boolean useDxInput = false;
|
||||||
|
|
||||||
|
public enum UseKotlinMethodsForVarNames {
|
||||||
|
DISABLE, APPLY, APPLY_AND_HIDE
|
||||||
|
}
|
||||||
|
|
||||||
|
private UseKotlinMethodsForVarNames useKotlinMethodsForVarNames = UseKotlinMethodsForVarNames.APPLY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't save files (can be using for performance testing)
|
||||||
|
*/
|
||||||
|
private boolean skipFilesSave = false;
|
||||||
|
|
||||||
|
private Map<String, String> pluginOptions = new HashMap<>();
|
||||||
|
|
||||||
|
private JadxPluginLoader pluginLoader = new JadxBasePluginLoader();
|
||||||
|
|
||||||
|
private boolean loadJadxClsSetFile = true;
|
||||||
|
|
||||||
|
public JadxArgs() {
|
||||||
|
// use default options
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRootDir(File rootDir) {
|
||||||
|
setOutDir(rootDir);
|
||||||
|
setOutDirSrc(new File(rootDir, DEFAULT_SRC_DIR));
|
||||||
|
setOutDirRes(new File(rootDir, DEFAULT_RES_DIR));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
try {
|
||||||
|
inputFiles = null;
|
||||||
|
if (codeCache != null) {
|
||||||
|
codeCache.close();
|
||||||
|
}
|
||||||
|
if (usageInfoCache != null) {
|
||||||
|
usageInfoCache.close();
|
||||||
|
}
|
||||||
|
if (pluginLoader != null) {
|
||||||
|
pluginLoader.close();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Failed to close JadxArgs", e);
|
||||||
|
} finally {
|
||||||
|
codeCache = null;
|
||||||
|
usageInfoCache = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<File> getInputFiles() {
|
||||||
|
return inputFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputFile(File inputFile) {
|
||||||
|
this.inputFiles = Collections.singletonList(inputFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputFiles(List<File> inputFiles) {
|
||||||
|
this.inputFiles = inputFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getOutDir() {
|
||||||
|
return outDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutDir(File outDir) {
|
||||||
|
this.outDir = outDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getOutDirSrc() {
|
||||||
|
return outDirSrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutDirSrc(File outDirSrc) {
|
||||||
|
this.outDirSrc = outDirSrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getOutDirRes() {
|
||||||
|
return outDirRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutDirRes(File outDirRes) {
|
||||||
|
this.outDirRes = outDirRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getThreadsCount() {
|
||||||
|
return threadsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThreadsCount(int threadsCount) {
|
||||||
|
this.threadsCount = Math.max(1, threadsCount); // make sure threadsCount >= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCfgOutput() {
|
||||||
|
return cfgOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCfgOutput(boolean cfgOutput) {
|
||||||
|
this.cfgOutput = cfgOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRawCFGOutput() {
|
||||||
|
return rawCFGOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRawCFGOutput(boolean rawCFGOutput) {
|
||||||
|
this.rawCFGOutput = rawCFGOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFallbackMode() {
|
||||||
|
return decompilationMode == DecompilationMode.FALLBACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deprecated: use 'decompilation mode' property
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setFallbackMode(boolean fallbackMode) {
|
||||||
|
if (fallbackMode) {
|
||||||
|
this.decompilationMode = DecompilationMode.FALLBACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShowInconsistentCode() {
|
||||||
|
return showInconsistentCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowInconsistentCode(boolean showInconsistentCode) {
|
||||||
|
this.showInconsistentCode = showInconsistentCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseImports() {
|
||||||
|
return useImports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseImports(boolean useImports) {
|
||||||
|
this.useImports = useImports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDebugInfo() {
|
||||||
|
return debugInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDebugInfo(boolean debugInfo) {
|
||||||
|
this.debugInfo = debugInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInsertDebugLines() {
|
||||||
|
return insertDebugLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInsertDebugLines(boolean insertDebugLines) {
|
||||||
|
this.insertDebugLines = insertDebugLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInlineAnonymousClasses() {
|
||||||
|
return inlineAnonymousClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInlineAnonymousClasses(boolean inlineAnonymousClasses) {
|
||||||
|
this.inlineAnonymousClasses = inlineAnonymousClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInlineMethods() {
|
||||||
|
return inlineMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInlineMethods(boolean inlineMethods) {
|
||||||
|
this.inlineMethods = inlineMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllowInlineKotlinLambda() {
|
||||||
|
return allowInlineKotlinLambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) {
|
||||||
|
this.allowInlineKotlinLambda = allowInlineKotlinLambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMoveInnerClasses() {
|
||||||
|
return moveInnerClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMoveInnerClasses(boolean moveInnerClasses) {
|
||||||
|
this.moveInnerClasses = moveInnerClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExtractFinally() {
|
||||||
|
return extractFinally;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtractFinally(boolean extractFinally) {
|
||||||
|
this.extractFinally = extractFinally;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSkipResources() {
|
||||||
|
return skipResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkipResources(boolean skipResources) {
|
||||||
|
this.skipResources = skipResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSkipSources() {
|
||||||
|
return skipSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkipSources(boolean skipSources) {
|
||||||
|
this.skipSources = skipSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIncludeDependencies(boolean includeDependencies) {
|
||||||
|
this.includeDependencies = includeDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIncludeDependencies() {
|
||||||
|
return includeDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Predicate<String> getClassFilter() {
|
||||||
|
return classFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClassFilter(Predicate<String> classFilter) {
|
||||||
|
this.classFilter = classFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getUserRenamesMappingsPath() {
|
||||||
|
return userRenamesMappingsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserRenamesMappingsPath(Path path) {
|
||||||
|
this.userRenamesMappingsPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRenamesMappingsMode getUserRenamesMappingsMode() {
|
||||||
|
return userRenamesMappingsMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserRenamesMappingsMode(UserRenamesMappingsMode mode) {
|
||||||
|
this.userRenamesMappingsMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeobfuscationOn() {
|
||||||
|
return deobfuscationOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeobfuscationOn(boolean deobfuscationOn) {
|
||||||
|
this.deobfuscationOn = deobfuscationOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeobfuscationForceSave() {
|
||||||
|
return generatedRenamesMappingFileMode == GeneratedRenamesMappingFileMode.OVERWRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeobfuscationForceSave(boolean deobfuscationForceSave) {
|
||||||
|
if (deobfuscationForceSave) {
|
||||||
|
this.generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.OVERWRITE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {
|
||||||
|
return generatedRenamesMappingFileMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode mode) {
|
||||||
|
this.generatedRenamesMappingFileMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseSourceNameAsClassAlias() {
|
||||||
|
return useSourceNameAsClassAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseSourceNameAsClassAlias(boolean useSourceNameAsClassAlias) {
|
||||||
|
this.useSourceNameAsClassAlias = useSourceNameAsClassAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeobfuscationMinLength() {
|
||||||
|
return deobfuscationMinLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeobfuscationMinLength(int deobfuscationMinLength) {
|
||||||
|
this.deobfuscationMinLength = deobfuscationMinLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeobfuscationMaxLength() {
|
||||||
|
return deobfuscationMaxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeobfuscationMaxLength(int deobfuscationMaxLength) {
|
||||||
|
this.deobfuscationMaxLength = deobfuscationMaxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getDeobfuscationWhitelist() {
|
||||||
|
return this.deobfuscationWhitelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeobfuscationWhitelist(List<String> deobfuscationWhitelist) {
|
||||||
|
this.deobfuscationWhitelist = deobfuscationWhitelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getGeneratedRenamesMappingFile() {
|
||||||
|
return generatedRenamesMappingFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGeneratedRenamesMappingFile(File file) {
|
||||||
|
this.generatedRenamesMappingFile = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceNameSource getResourceNameSource() {
|
||||||
|
return resourceNameSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourceNameSource(ResourceNameSource resourceNameSource) {
|
||||||
|
this.resourceNameSource = resourceNameSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAliasProvider getAliasProvider() {
|
||||||
|
return aliasProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAliasProvider(IAliasProvider aliasProvider) {
|
||||||
|
this.aliasProvider = aliasProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRenameCondition getRenameCondition() {
|
||||||
|
return renameCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenameCondition(IRenameCondition renameCondition) {
|
||||||
|
this.renameCondition = renameCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEscapeUnicode() {
|
||||||
|
return escapeUnicode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEscapeUnicode(boolean escapeUnicode) {
|
||||||
|
this.escapeUnicode = escapeUnicode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReplaceConsts() {
|
||||||
|
return replaceConsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReplaceConsts(boolean replaceConsts) {
|
||||||
|
this.replaceConsts = replaceConsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRespectBytecodeAccModifiers() {
|
||||||
|
return respectBytecodeAccModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRespectBytecodeAccModifiers(boolean respectBytecodeAccModifiers) {
|
||||||
|
this.respectBytecodeAccModifiers = respectBytecodeAccModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExportAsGradleProject() {
|
||||||
|
return exportAsGradleProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExportAsGradleProject(boolean exportAsGradleProject) {
|
||||||
|
this.exportAsGradleProject = exportAsGradleProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSkipXmlPrettyPrint() {
|
||||||
|
return skipXmlPrettyPrint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkipXmlPrettyPrint(boolean skipXmlPrettyPrint) {
|
||||||
|
this.skipXmlPrettyPrint = skipXmlPrettyPrint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFsCaseSensitive() {
|
||||||
|
return fsCaseSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFsCaseSensitive(boolean fsCaseSensitive) {
|
||||||
|
this.fsCaseSensitive = fsCaseSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRenameCaseSensitive() {
|
||||||
|
return renameFlags.contains(RenameEnum.CASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenameCaseSensitive(boolean renameCaseSensitive) {
|
||||||
|
updateRenameFlag(renameCaseSensitive, RenameEnum.CASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRenameValid() {
|
||||||
|
return renameFlags.contains(RenameEnum.VALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenameValid(boolean renameValid) {
|
||||||
|
updateRenameFlag(renameValid, RenameEnum.VALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRenamePrintable() {
|
||||||
|
return renameFlags.contains(RenameEnum.PRINTABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenamePrintable(boolean renamePrintable) {
|
||||||
|
updateRenameFlag(renamePrintable, RenameEnum.PRINTABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateRenameFlag(boolean enabled, RenameEnum flag) {
|
||||||
|
if (enabled) {
|
||||||
|
renameFlags.add(flag);
|
||||||
|
} else {
|
||||||
|
renameFlags.remove(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenameFlags(Set<RenameEnum> renameFlags) {
|
||||||
|
this.renameFlags = renameFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<RenameEnum> getRenameFlags() {
|
||||||
|
return renameFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputFormatEnum getOutputFormat() {
|
||||||
|
return outputFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isJsonOutput() {
|
||||||
|
return outputFormat == OutputFormatEnum.JSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputFormat(OutputFormatEnum outputFormat) {
|
||||||
|
this.outputFormat = outputFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecompilationMode getDecompilationMode() {
|
||||||
|
return decompilationMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecompilationMode(DecompilationMode decompilationMode) {
|
||||||
|
this.decompilationMode = decompilationMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICodeCache getCodeCache() {
|
||||||
|
return codeCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeCache(ICodeCache codeCache) {
|
||||||
|
this.codeCache = codeCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function<JadxArgs, ICodeWriter> getCodeWriterProvider() {
|
||||||
|
return codeWriterProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeWriterProvider(Function<JadxArgs, ICodeWriter> codeWriterProvider) {
|
||||||
|
this.codeWriterProvider = codeWriterProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUsageInfoCache getUsageInfoCache() {
|
||||||
|
return usageInfoCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsageInfoCache(IUsageInfoCache usageInfoCache) {
|
||||||
|
this.usageInfoCache = usageInfoCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICodeData getCodeData() {
|
||||||
|
return codeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeData(ICodeData codeData) {
|
||||||
|
this.codeData = codeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommentsLevel getCommentsLevel() {
|
||||||
|
return commentsLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCommentsLevel(CommentsLevel commentsLevel) {
|
||||||
|
this.commentsLevel = commentsLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegerFormat getIntegerFormat() {
|
||||||
|
return integerFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntegerFormat(IntegerFormat format) {
|
||||||
|
this.integerFormat = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseDxInput() {
|
||||||
|
return useDxInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseDxInput(boolean useDxInput) {
|
||||||
|
this.useDxInput = useDxInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() {
|
||||||
|
return useKotlinMethodsForVarNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseKotlinMethodsForVarNames(UseKotlinMethodsForVarNames useKotlinMethodsForVarNames) {
|
||||||
|
this.useKotlinMethodsForVarNames = useKotlinMethodsForVarNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSkipFilesSave() {
|
||||||
|
return skipFilesSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkipFilesSave(boolean skipFilesSave) {
|
||||||
|
this.skipFilesSave = skipFilesSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getPluginOptions() {
|
||||||
|
return pluginOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPluginOptions(Map<String, String> pluginOptions) {
|
||||||
|
this.pluginOptions = pluginOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JadxPluginLoader getPluginLoader() {
|
||||||
|
return pluginLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPluginLoader(JadxPluginLoader pluginLoader) {
|
||||||
|
this.pluginLoader = pluginLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLoadJadxClsSetFile() {
|
||||||
|
return loadJadxClsSetFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoadJadxClsSetFile(boolean loadJadxClsSetFile) {
|
||||||
|
this.loadJadxClsSetFile = loadJadxClsSetFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash of all options that can change result code
|
||||||
|
*/
|
||||||
|
public String makeCodeArgsHash(@Nullable JadxDecompiler decompiler) {
|
||||||
|
String argStr = "args:" + decompilationMode + useImports + showInconsistentCode
|
||||||
|
+ inlineAnonymousClasses + inlineMethods + moveInnerClasses + allowInlineKotlinLambda
|
||||||
|
+ deobfuscationOn + deobfuscationMinLength + deobfuscationMaxLength + deobfuscationWhitelist
|
||||||
|
+ resourceNameSource
|
||||||
|
+ useKotlinMethodsForVarNames
|
||||||
|
+ insertDebugLines + extractFinally
|
||||||
|
+ debugInfo + useSourceNameAsClassAlias + escapeUnicode + replaceConsts
|
||||||
|
+ respectBytecodeAccModifiers + fsCaseSensitive + renameFlags
|
||||||
|
+ commentsLevel + useDxInput + integerFormat
|
||||||
|
+ "|" + buildPluginsHash(decompiler);
|
||||||
|
return FileUtils.md5Sum(argStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildPluginsHash(@Nullable JadxDecompiler decompiler) {
|
||||||
|
if (decompiler == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return decompiler.getPluginManager().getResolvedPluginContexts()
|
||||||
|
.stream()
|
||||||
|
.map(PluginContext::getInputsHash)
|
||||||
|
.collect(Collectors.joining(":"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "JadxArgs{" + "inputFiles=" + inputFiles
|
||||||
|
+ ", outDir=" + outDir
|
||||||
|
+ ", outDirSrc=" + outDirSrc
|
||||||
|
+ ", outDirRes=" + outDirRes
|
||||||
|
+ ", threadsCount=" + threadsCount
|
||||||
|
+ ", decompilationMode=" + decompilationMode
|
||||||
|
+ ", showInconsistentCode=" + showInconsistentCode
|
||||||
|
+ ", useImports=" + useImports
|
||||||
|
+ ", skipResources=" + skipResources
|
||||||
|
+ ", skipSources=" + skipSources
|
||||||
|
+ ", includeDependencies=" + includeDependencies
|
||||||
|
+ ", userRenamesMappingsPath=" + userRenamesMappingsPath
|
||||||
|
+ ", userRenamesMappingsMode=" + userRenamesMappingsMode
|
||||||
|
+ ", deobfuscationOn=" + deobfuscationOn
|
||||||
|
+ ", generatedRenamesMappingFile=" + generatedRenamesMappingFile
|
||||||
|
+ ", generatedRenamesMappingFileMode=" + generatedRenamesMappingFileMode
|
||||||
|
+ ", resourceNameSource=" + resourceNameSource
|
||||||
|
+ ", useSourceNameAsClassAlias=" + useSourceNameAsClassAlias
|
||||||
|
+ ", useKotlinMethodsForVarNames=" + useKotlinMethodsForVarNames
|
||||||
|
+ ", insertDebugLines=" + insertDebugLines
|
||||||
|
+ ", extractFinally=" + extractFinally
|
||||||
|
+ ", deobfuscationMinLength=" + deobfuscationMinLength
|
||||||
|
+ ", deobfuscationMaxLength=" + deobfuscationMaxLength
|
||||||
|
+ ", deobfuscationWhitelist=" + deobfuscationWhitelist
|
||||||
|
+ ", escapeUnicode=" + escapeUnicode
|
||||||
|
+ ", replaceConsts=" + replaceConsts
|
||||||
|
+ ", respectBytecodeAccModifiers=" + respectBytecodeAccModifiers
|
||||||
|
+ ", exportAsGradleProject=" + exportAsGradleProject
|
||||||
|
+ ", skipXmlPrettyPrint=" + skipXmlPrettyPrint
|
||||||
|
+ ", fsCaseSensitive=" + fsCaseSensitive
|
||||||
|
+ ", renameFlags=" + renameFlags
|
||||||
|
+ ", outputFormat=" + outputFormat
|
||||||
|
+ ", commentsLevel=" + commentsLevel
|
||||||
|
+ ", codeCache=" + codeCache
|
||||||
|
+ ", codeWriter=" + codeWriterProvider.apply(this).getClass().getSimpleName()
|
||||||
|
+ ", useDxInput=" + useDxInput
|
||||||
|
+ ", pluginOptions=" + pluginOptions
|
||||||
|
+ ", cfgOutput=" + cfgOutput
|
||||||
|
+ ", rawCFGOutput=" + rawCFGOutput
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.core.utils.exceptions.JadxArgsValidateException;
|
||||||
|
|
||||||
|
public class JadxArgsValidator {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidator.class);
|
||||||
|
|
||||||
|
public static void validate(JadxDecompiler jadx) {
|
||||||
|
JadxArgs args = jadx.getArgs();
|
||||||
|
checkInputFiles(jadx, args);
|
||||||
|
validateOutDirs(args);
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("Effective jadx args: {}", args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkInputFiles(JadxDecompiler jadx, JadxArgs args) {
|
||||||
|
List<File> inputFiles = args.getInputFiles();
|
||||||
|
if (inputFiles.isEmpty() && jadx.getCustomCodeLoaders().isEmpty()) {
|
||||||
|
throw new JadxArgsValidateException("Please specify input file");
|
||||||
|
}
|
||||||
|
for (File inputFile : inputFiles) {
|
||||||
|
String fileName = inputFile.getName();
|
||||||
|
if (fileName.startsWith("--")) {
|
||||||
|
throw new JadxArgsValidateException("Unknown argument: " + fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (File file : inputFiles) {
|
||||||
|
checkFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateOutDirs(JadxArgs args) {
|
||||||
|
File outDir = args.getOutDir();
|
||||||
|
File srcDir = args.getOutDirSrc();
|
||||||
|
File resDir = args.getOutDirRes();
|
||||||
|
if (outDir == null) {
|
||||||
|
if (srcDir != null) {
|
||||||
|
outDir = srcDir;
|
||||||
|
} else if (resDir != null) {
|
||||||
|
outDir = resDir;
|
||||||
|
} else {
|
||||||
|
outDir = makeDirFromInput(args);
|
||||||
|
}
|
||||||
|
args.setOutDir(outDir);
|
||||||
|
}
|
||||||
|
if (srcDir == null) {
|
||||||
|
args.setOutDirSrc(new File(args.getOutDir(), JadxArgs.DEFAULT_SRC_DIR));
|
||||||
|
}
|
||||||
|
if (resDir == null) {
|
||||||
|
args.setOutDirRes(new File(args.getOutDir(), JadxArgs.DEFAULT_RES_DIR));
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDir(args.getOutDir(), "Output");
|
||||||
|
checkDir(args.getOutDirSrc(), "Source output");
|
||||||
|
checkDir(args.getOutDirRes(), "Resources output");
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static File makeDirFromInput(JadxArgs args) {
|
||||||
|
String outDirName;
|
||||||
|
List<File> inputFiles = args.getInputFiles();
|
||||||
|
if (inputFiles.isEmpty()) {
|
||||||
|
outDirName = JadxArgs.DEFAULT_OUT_DIR;
|
||||||
|
} else {
|
||||||
|
File file = inputFiles.get(0);
|
||||||
|
String name = file.getName();
|
||||||
|
int pos = name.lastIndexOf('.');
|
||||||
|
if (pos != -1) {
|
||||||
|
outDirName = name.substring(0, pos);
|
||||||
|
} else {
|
||||||
|
outDirName = name + '-' + JadxArgs.DEFAULT_OUT_DIR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.info("output directory: {}", outDirName);
|
||||||
|
return new File(outDirName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkFile(File file) {
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new JadxArgsValidateException("File not found " + file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkDir(File dir, String desc) {
|
||||||
|
if (dir != null && dir.exists() && !dir.isDirectory()) {
|
||||||
|
throw new JadxArgsValidateException(desc + " directory exists as file " + dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JadxArgsValidator() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,711 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
|
import jadx.api.metadata.annotations.NodeDeclareRef;
|
||||||
|
import jadx.api.metadata.annotations.VarNode;
|
||||||
|
import jadx.api.metadata.annotations.VarRef;
|
||||||
|
import jadx.api.plugins.CustomResourcesLoader;
|
||||||
|
import jadx.api.plugins.JadxPlugin;
|
||||||
|
import jadx.api.plugins.events.IJadxEvents;
|
||||||
|
import jadx.api.plugins.input.ICodeLoader;
|
||||||
|
import jadx.api.plugins.input.JadxCodeInput;
|
||||||
|
import jadx.api.plugins.pass.JadxPass;
|
||||||
|
import jadx.api.plugins.pass.types.JadxAfterLoadPass;
|
||||||
|
import jadx.api.plugins.pass.types.JadxPassType;
|
||||||
|
import jadx.api.utils.tasks.ITaskExecutor;
|
||||||
|
import jadx.core.Jadx;
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
import jadx.core.dex.nodes.PackageNode;
|
||||||
|
import jadx.core.dex.nodes.RootNode;
|
||||||
|
import jadx.core.dex.visitors.SaveCode;
|
||||||
|
import jadx.core.export.ExportGradleTask;
|
||||||
|
import jadx.core.plugins.JadxPluginManager;
|
||||||
|
import jadx.core.plugins.PluginContext;
|
||||||
|
import jadx.core.plugins.events.JadxEventsImpl;
|
||||||
|
import jadx.core.utils.DecompilerScheduler;
|
||||||
|
import jadx.core.utils.Utils;
|
||||||
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
import jadx.core.utils.files.FileUtils;
|
||||||
|
import jadx.core.utils.tasks.TaskExecutor;
|
||||||
|
import jadx.core.xmlgen.BinaryXMLParser;
|
||||||
|
import jadx.core.xmlgen.ProtoXMLParser;
|
||||||
|
import jadx.core.xmlgen.ResourcesSaver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jadx API usage example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* JadxArgs args = new JadxArgs();
|
||||||
|
* args.getInputFiles().add(new File("test.apk"));
|
||||||
|
* args.setOutDir(new File("jadx-test-output"));
|
||||||
|
* try (JadxDecompiler jadx = new JadxDecompiler(args)) {
|
||||||
|
* jadx.load();
|
||||||
|
* jadx.save();
|
||||||
|
* }
|
||||||
|
* </code>
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* Instead of 'save()' you can iterate over decompiled classes:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
|
* for(JavaClass cls : jadx.getClasses()) {
|
||||||
|
* System.out.println(cls.getCode());
|
||||||
|
* }
|
||||||
|
* </code>
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public final class JadxDecompiler implements Closeable {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
|
||||||
|
|
||||||
|
private final JadxArgs args;
|
||||||
|
private final JadxPluginManager pluginManager = new JadxPluginManager(this);
|
||||||
|
private final List<ICodeLoader> loadedInputs = new ArrayList<>();
|
||||||
|
|
||||||
|
private RootNode root;
|
||||||
|
private List<JavaClass> classes;
|
||||||
|
private List<ResourceFile> resources;
|
||||||
|
|
||||||
|
private BinaryXMLParser binaryXmlParser;
|
||||||
|
private ProtoXMLParser protoXmlParser;
|
||||||
|
|
||||||
|
private final IDecompileScheduler decompileScheduler = new DecompilerScheduler();
|
||||||
|
private final JadxEventsImpl events = new JadxEventsImpl();
|
||||||
|
|
||||||
|
private final List<ICodeLoader> customCodeLoaders = new ArrayList<>();
|
||||||
|
private final List<CustomResourcesLoader> customResourcesLoaders = new ArrayList<>();
|
||||||
|
private final Map<JadxPassType, List<JadxPass>> customPasses = new HashMap<>();
|
||||||
|
|
||||||
|
public JadxDecompiler() {
|
||||||
|
this(new JadxArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JadxDecompiler(JadxArgs args) {
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load() {
|
||||||
|
reset();
|
||||||
|
JadxArgsValidator.validate(this);
|
||||||
|
LOG.info("loading ...");
|
||||||
|
loadPlugins();
|
||||||
|
loadInputFiles();
|
||||||
|
|
||||||
|
root = new RootNode(args);
|
||||||
|
root.init();
|
||||||
|
root.setDecompilerRef(this);
|
||||||
|
root.mergePasses(customPasses);
|
||||||
|
root.loadClasses(loadedInputs);
|
||||||
|
root.initClassPath();
|
||||||
|
root.loadResources(getResources());
|
||||||
|
root.runPreDecompileStage();
|
||||||
|
root.initPasses();
|
||||||
|
loadFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadPasses() {
|
||||||
|
LOG.info("reloading (passes only) ...");
|
||||||
|
customPasses.clear();
|
||||||
|
root.resetPasses();
|
||||||
|
events.reset();
|
||||||
|
loadPlugins();
|
||||||
|
root.mergePasses(customPasses);
|
||||||
|
root.restartVisitors();
|
||||||
|
root.initPasses();
|
||||||
|
loadFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadInputFiles() {
|
||||||
|
loadedInputs.clear();
|
||||||
|
List<Path> inputPaths = Utils.collectionMap(args.getInputFiles(), File::toPath);
|
||||||
|
List<Path> inputFiles = FileUtils.expandDirs(inputPaths);
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
for (PluginContext plugin : pluginManager.getResolvedPluginContexts()) {
|
||||||
|
for (JadxCodeInput codeLoader : plugin.getCodeInputs()) {
|
||||||
|
try {
|
||||||
|
ICodeLoader loader = codeLoader.loadFiles(inputFiles);
|
||||||
|
if (loader != null && !loader.isEmpty()) {
|
||||||
|
loadedInputs.add(loader);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new JadxRuntimeException("Failed to load code for plugin: " + plugin, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadedInputs.addAll(customCodeLoaders);
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("Loaded using {} inputs plugin in {} ms", loadedInputs.size(), System.currentTimeMillis() - start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
root = null;
|
||||||
|
classes = null;
|
||||||
|
resources = null;
|
||||||
|
binaryXmlParser = null;
|
||||||
|
protoXmlParser = null;
|
||||||
|
events.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
reset();
|
||||||
|
closeInputs();
|
||||||
|
closeLoaders();
|
||||||
|
args.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeInputs() {
|
||||||
|
loadedInputs.forEach(load -> {
|
||||||
|
try {
|
||||||
|
load.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Failed to close input", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
loadedInputs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeLoaders() {
|
||||||
|
for (CustomResourcesLoader resourcesLoader : customResourcesLoaders) {
|
||||||
|
try {
|
||||||
|
resourcesLoader.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Failed to close resource loader: " + resourcesLoader, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customResourcesLoaders.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPlugins() {
|
||||||
|
pluginManager.providesSuggestion("java-input", args.isUseDxInput() ? "java-convert" : "java-input");
|
||||||
|
pluginManager.load(args.getPluginLoader());
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("Resolved plugins: {}", pluginManager.getResolvedPluginContexts());
|
||||||
|
}
|
||||||
|
pluginManager.initResolved();
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
List<String> passes = customPasses.values().stream().flatMap(Collection::stream)
|
||||||
|
.map(p -> p.getInfo().getName()).collect(Collectors.toList());
|
||||||
|
LOG.debug("Loaded custom passes: {} {}", passes.size(), passes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFinished() {
|
||||||
|
LOG.debug("Load finished");
|
||||||
|
List<JadxPass> list = customPasses.get(JadxAfterLoadPass.TYPE);
|
||||||
|
if (list != null) {
|
||||||
|
for (JadxPass pass : list) {
|
||||||
|
((JadxAfterLoadPass) pass).init(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public void registerPlugin(JadxPlugin plugin) {
|
||||||
|
pluginManager.register(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getVersion() {
|
||||||
|
return Jadx.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
save(!args.isSkipSources(), !args.isSkipResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ProgressListener {
|
||||||
|
void progress(long done, long total);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("BusyWait")
|
||||||
|
public void save(int intervalInMillis, ProgressListener listener) {
|
||||||
|
try {
|
||||||
|
ITaskExecutor tasks = getSaveTaskExecutor();
|
||||||
|
tasks.execute();
|
||||||
|
long total = tasks.getTasksCount();
|
||||||
|
while (tasks.isRunning()) {
|
||||||
|
listener.progress(tasks.getProgress(), total);
|
||||||
|
Thread.sleep(intervalInMillis);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("Save interrupted", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveSources() {
|
||||||
|
save(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveResources() {
|
||||||
|
save(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void save(boolean saveSources, boolean saveResources) {
|
||||||
|
ITaskExecutor executor = getSaveTasks(saveSources, saveResources);
|
||||||
|
executor.execute();
|
||||||
|
executor.awaitTermination();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITaskExecutor getSaveTaskExecutor() {
|
||||||
|
return getSaveTasks(!args.isSkipSources(), !args.isSkipResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
public ExecutorService getSaveExecutor() {
|
||||||
|
ITaskExecutor executor = getSaveTaskExecutor();
|
||||||
|
executor.execute();
|
||||||
|
return executor.getInternalExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
public List<Runnable> getSaveTasks() {
|
||||||
|
return Collections.singletonList(this::save);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskExecutor getSaveTasks(boolean saveSources, boolean saveResources) {
|
||||||
|
if (root == null) {
|
||||||
|
throw new JadxRuntimeException("No loaded files");
|
||||||
|
}
|
||||||
|
File sourcesOutDir;
|
||||||
|
File resOutDir;
|
||||||
|
ExportGradleTask gradleExportTask;
|
||||||
|
if (args.isExportAsGradleProject()) {
|
||||||
|
gradleExportTask = new ExportGradleTask(resources, root, args.getOutDir());
|
||||||
|
gradleExportTask.init();
|
||||||
|
sourcesOutDir = gradleExportTask.getSrcOutDir();
|
||||||
|
resOutDir = gradleExportTask.getResOutDir();
|
||||||
|
} else {
|
||||||
|
sourcesOutDir = args.getOutDirSrc();
|
||||||
|
resOutDir = args.getOutDirRes();
|
||||||
|
gradleExportTask = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskExecutor executor = new TaskExecutor();
|
||||||
|
executor.setThreadsCount(args.getThreadsCount());
|
||||||
|
if (saveResources) {
|
||||||
|
// save resources first because decompilation can stop or fail
|
||||||
|
appendResourcesSaveTasks(executor, resOutDir);
|
||||||
|
}
|
||||||
|
if (saveSources) {
|
||||||
|
appendSourcesSave(executor, sourcesOutDir);
|
||||||
|
}
|
||||||
|
if (gradleExportTask != null) {
|
||||||
|
executor.addSequentialTask(gradleExportTask);
|
||||||
|
}
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendResourcesSaveTasks(ITaskExecutor executor, File outDir) {
|
||||||
|
if (args.isSkipFilesSave()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// process AndroidManifest.xml first to load complete resource ids table
|
||||||
|
for (ResourceFile resourceFile : getResources()) {
|
||||||
|
if (resourceFile.getType() == ResourceType.MANIFEST) {
|
||||||
|
new ResourcesSaver(outDir, resourceFile).run();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Set<String> inputFileNames = args.getInputFiles().stream()
|
||||||
|
.map(File::getAbsolutePath)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
List<Runnable> tasks = new ArrayList<>();
|
||||||
|
for (ResourceFile resourceFile : getResources()) {
|
||||||
|
ResourceType resType = resourceFile.getType();
|
||||||
|
if (resType == ResourceType.MANIFEST) {
|
||||||
|
// already processed
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (resType != ResourceType.ARSC
|
||||||
|
&& inputFileNames.contains(resourceFile.getOriginalName())) {
|
||||||
|
// ignore resource made from input file
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tasks.add(new ResourcesSaver(outDir, resourceFile));
|
||||||
|
}
|
||||||
|
executor.addParallelTasks(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendSourcesSave(ITaskExecutor executor, File outDir) {
|
||||||
|
List<JavaClass> classes = getClasses();
|
||||||
|
List<JavaClass> processQueue = filterClasses(classes);
|
||||||
|
List<List<JavaClass>> batches;
|
||||||
|
try {
|
||||||
|
batches = decompileScheduler.buildBatches(processQueue);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new JadxRuntimeException("Decompilation batches build failed", e);
|
||||||
|
}
|
||||||
|
List<Runnable> decompileTasks = new ArrayList<>(batches.size());
|
||||||
|
for (List<JavaClass> decompileBatch : batches) {
|
||||||
|
decompileTasks.add(() -> {
|
||||||
|
for (JavaClass cls : decompileBatch) {
|
||||||
|
try {
|
||||||
|
ClassNode clsNode = cls.getClassNode();
|
||||||
|
ICodeInfo code = clsNode.getCode();
|
||||||
|
SaveCode.save(outDir, clsNode, code);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Error saving class: {}", cls, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
executor.addParallelTasks(decompileTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<JavaClass> filterClasses(List<JavaClass> classes) {
|
||||||
|
Predicate<String> classFilter = args.getClassFilter();
|
||||||
|
List<JavaClass> list = new ArrayList<>(classes.size());
|
||||||
|
for (JavaClass cls : classes) {
|
||||||
|
ClassNode clsNode = cls.getClassNode();
|
||||||
|
if (clsNode.contains(AFlag.DONT_GENERATE)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (classFilter != null && !classFilter.test(clsNode.getClassInfo().getFullName())) {
|
||||||
|
if (!args.isIncludeDependencies()) {
|
||||||
|
clsNode.add(AFlag.DONT_GENERATE);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
list.add(cls);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JavaClass> getClasses() {
|
||||||
|
if (root == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
if (classes == null) {
|
||||||
|
List<ClassNode> classNodeList = root.getClasses();
|
||||||
|
List<JavaClass> clsList = new ArrayList<>(classNodeList.size());
|
||||||
|
for (ClassNode classNode : classNodeList) {
|
||||||
|
if (classNode.contains(AFlag.DONT_GENERATE)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!classNode.getClassInfo().isInner()) {
|
||||||
|
clsList.add(convertClassNode(classNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
classes = Collections.unmodifiableList(clsList);
|
||||||
|
}
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JavaClass> getClassesWithInners() {
|
||||||
|
return Utils.collectionMap(root.getClasses(), this::convertClassNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List<ResourceFile> getResources() {
|
||||||
|
if (resources == null) {
|
||||||
|
if (root == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
resources = new ResourcesLoader(this).load();
|
||||||
|
}
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JavaPackage> getPackages() {
|
||||||
|
return Utils.collectionMap(root.getPackages(), this::convertPackageNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrorsCount() {
|
||||||
|
if (root == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return root.getErrorsCounter().getErrorCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWarnsCount() {
|
||||||
|
if (root == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return root.getErrorsCounter().getWarnsCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printErrorsReport() {
|
||||||
|
if (root == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.getClsp().printMissingClasses();
|
||||||
|
root.getErrorsCounter().printReport();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal API. Not Stable!
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public RootNode getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized BinaryXMLParser getBinaryXmlParser() {
|
||||||
|
if (binaryXmlParser == null) {
|
||||||
|
binaryXmlParser = new BinaryXMLParser(root);
|
||||||
|
}
|
||||||
|
return binaryXmlParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized ProtoXMLParser getProtoXmlParser() {
|
||||||
|
if (protoXmlParser == null) {
|
||||||
|
protoXmlParser = new ProtoXMLParser(root);
|
||||||
|
}
|
||||||
|
return protoXmlParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get JavaClass by ClassNode without loading and decompilation
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
synchronized JavaClass convertClassNode(ClassNode cls) {
|
||||||
|
JavaClass javaClass = cls.getJavaNode();
|
||||||
|
if (javaClass == null) {
|
||||||
|
javaClass = cls.isInner()
|
||||||
|
? new JavaClass(cls, convertClassNode(cls.getParentClass()))
|
||||||
|
: new JavaClass(cls, this);
|
||||||
|
cls.setJavaNode(javaClass);
|
||||||
|
}
|
||||||
|
return javaClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
synchronized JavaField convertFieldNode(FieldNode fld) {
|
||||||
|
JavaField javaField = fld.getJavaNode();
|
||||||
|
if (javaField == null) {
|
||||||
|
JavaClass parentCls = convertClassNode(fld.getParentClass());
|
||||||
|
javaField = new JavaField(parentCls, fld);
|
||||||
|
fld.setJavaNode(javaField);
|
||||||
|
}
|
||||||
|
return javaField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
synchronized JavaMethod convertMethodNode(MethodNode mth) {
|
||||||
|
JavaMethod javaMethod = mth.getJavaNode();
|
||||||
|
if (javaMethod == null) {
|
||||||
|
javaMethod = new JavaMethod(convertClassNode(mth.getParentClass()), mth);
|
||||||
|
mth.setJavaNode(javaMethod);
|
||||||
|
}
|
||||||
|
return javaMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
synchronized JavaPackage convertPackageNode(PackageNode pkg) {
|
||||||
|
JavaPackage foundPkg = pkg.getJavaNode();
|
||||||
|
if (foundPkg != null) {
|
||||||
|
return foundPkg;
|
||||||
|
}
|
||||||
|
List<JavaClass> clsList = Utils.collectionMap(pkg.getClasses(), this::convertClassNode);
|
||||||
|
int subPkgsCount = pkg.getSubPackages().size();
|
||||||
|
List<JavaPackage> subPkgs = subPkgsCount == 0 ? Collections.emptyList() : new ArrayList<>(subPkgsCount);
|
||||||
|
JavaPackage javaPkg = new JavaPackage(pkg, clsList, subPkgs);
|
||||||
|
if (subPkgsCount != 0) {
|
||||||
|
// add subpackages after parent to avoid endless recursion
|
||||||
|
for (PackageNode subPackage : pkg.getSubPackages()) {
|
||||||
|
subPkgs.add(convertPackageNode(subPackage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pkg.setJavaNode(javaPkg);
|
||||||
|
return javaPkg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public JavaClass searchJavaClassByOrigFullName(String fullName) {
|
||||||
|
return getRoot().getClasses().stream()
|
||||||
|
.filter(cls -> cls.getClassInfo().getFullName().equals(fullName))
|
||||||
|
.findFirst()
|
||||||
|
.map(this::convertClassNode)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ClassNode searchClassNodeByOrigFullName(String fullName) {
|
||||||
|
return getRoot().getClasses().stream()
|
||||||
|
.filter(cls -> cls.getClassInfo().getFullName().equals(fullName))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns parent if class contains DONT_GENERATE flag.
|
||||||
|
@Nullable
|
||||||
|
public JavaClass searchJavaClassOrItsParentByOrigFullName(String fullName) {
|
||||||
|
ClassNode node = getRoot().getClasses().stream()
|
||||||
|
.filter(cls -> cls.getClassInfo().getFullName().equals(fullName))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
if (node != null) {
|
||||||
|
if (node.contains(AFlag.DONT_GENERATE)) {
|
||||||
|
return convertClassNode(node.getTopParentClass());
|
||||||
|
} else {
|
||||||
|
return convertClassNode(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public JavaClass searchJavaClassByAliasFullName(String fullName) {
|
||||||
|
return getRoot().getClasses().stream()
|
||||||
|
.filter(cls -> cls.getClassInfo().getAliasFullName().equals(fullName))
|
||||||
|
.findFirst()
|
||||||
|
.map(this::convertClassNode)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public JavaNode getJavaNodeByRef(ICodeNodeRef ann) {
|
||||||
|
return getJavaNodeByCodeAnnotation(null, ann);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public JavaNode getJavaNodeByCodeAnnotation(@Nullable ICodeInfo codeInfo, @Nullable ICodeAnnotation ann) {
|
||||||
|
if (ann == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (ann.getAnnType()) {
|
||||||
|
case CLASS:
|
||||||
|
return convertClassNode((ClassNode) ann);
|
||||||
|
case METHOD:
|
||||||
|
return convertMethodNode((MethodNode) ann);
|
||||||
|
case FIELD:
|
||||||
|
return convertFieldNode((FieldNode) ann);
|
||||||
|
case DECLARATION:
|
||||||
|
return getJavaNodeByCodeAnnotation(codeInfo, ((NodeDeclareRef) ann).getNode());
|
||||||
|
case VAR:
|
||||||
|
return resolveVarNode((VarNode) ann);
|
||||||
|
case VAR_REF:
|
||||||
|
return resolveVarRef(codeInfo, (VarRef) ann);
|
||||||
|
case OFFSET:
|
||||||
|
// offset annotation don't have java node object
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
throw new JadxRuntimeException("Unknown annotation type: " + ann.getAnnType() + ", class: " + ann.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JavaVariable resolveVarNode(VarNode varNode) {
|
||||||
|
JavaMethod javaNode = convertMethodNode(varNode.getMth());
|
||||||
|
return new JavaVariable(javaNode, varNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private JavaVariable resolveVarRef(ICodeInfo codeInfo, VarRef varRef) {
|
||||||
|
if (codeInfo == null) {
|
||||||
|
throw new JadxRuntimeException("Missing code info for resolve VarRef: " + varRef);
|
||||||
|
}
|
||||||
|
ICodeAnnotation varNodeAnn = codeInfo.getCodeMetadata().getAt(varRef.getRefPos());
|
||||||
|
if (varNodeAnn != null && varNodeAnn.getAnnType() == ICodeAnnotation.AnnType.DECLARATION) {
|
||||||
|
ICodeNodeRef nodeRef = ((NodeDeclareRef) varNodeAnn).getNode();
|
||||||
|
if (nodeRef.getAnnType() == ICodeAnnotation.AnnType.VAR) {
|
||||||
|
return resolveVarNode((VarNode) nodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<JavaNode> convertNodes(Collection<? extends ICodeNodeRef> nodesList) {
|
||||||
|
return nodesList.stream()
|
||||||
|
.map(this::getJavaNodeByRef)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public JavaNode getJavaNodeAtPosition(ICodeInfo codeInfo, int pos) {
|
||||||
|
ICodeAnnotation ann = codeInfo.getCodeMetadata().getAt(pos);
|
||||||
|
return getJavaNodeByCodeAnnotation(codeInfo, ann);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public JavaNode getClosestJavaNode(ICodeInfo codeInfo, int pos) {
|
||||||
|
ICodeAnnotation ann = codeInfo.getCodeMetadata().getClosestUp(pos);
|
||||||
|
return getJavaNodeByCodeAnnotation(codeInfo, ann);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public JavaNode getEnclosingNode(ICodeInfo codeInfo, int pos) {
|
||||||
|
ICodeNodeRef obj = codeInfo.getCodeMetadata().getNodeAt(pos);
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getJavaNodeByRef(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadCodeData() {
|
||||||
|
root.notifyCodeDataListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JadxArgs getArgs() {
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JadxPluginManager getPluginManager() {
|
||||||
|
return pluginManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDecompileScheduler getDecompileScheduler() {
|
||||||
|
return decompileScheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IJadxEvents events() {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCustomCodeLoader(ICodeLoader customCodeLoader) {
|
||||||
|
customCodeLoaders.add(customCodeLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ICodeLoader> getCustomCodeLoaders() {
|
||||||
|
return customCodeLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCustomResourcesLoader(CustomResourcesLoader loader) {
|
||||||
|
if (customResourcesLoaders.contains(loader)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
customResourcesLoaders.add(loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CustomResourcesLoader> getCustomResourcesLoaders() {
|
||||||
|
return customResourcesLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCustomPass(JadxPass pass) {
|
||||||
|
customPasses.computeIfAbsent(pass.getPassType(), l -> new ArrayList<>()).add(pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "jadx decompiler " + getVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,113 +1,359 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
import jadx.core.codegen.CodeWriter;
|
|
||||||
import jadx.core.dex.info.AccessInfo;
|
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public final class JavaClass {
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
private final Decompiler decompiler;
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
|
import jadx.core.dex.attributes.AType;
|
||||||
|
import jadx.core.dex.attributes.nodes.AnonymousClassAttr;
|
||||||
|
import jadx.core.dex.attributes.nodes.InlinedAttr;
|
||||||
|
import jadx.core.dex.info.AccessInfo;
|
||||||
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
import jadx.core.utils.ListUtils;
|
||||||
|
|
||||||
|
public final class JavaClass implements JavaNode {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JavaClass.class);
|
||||||
|
|
||||||
|
private final JadxDecompiler decompiler;
|
||||||
private final ClassNode cls;
|
private final ClassNode cls;
|
||||||
private final List<JavaClass> innerClasses;
|
private final JavaClass parent;
|
||||||
private final List<JavaField> fields;
|
|
||||||
private final List<JavaMethod> methods;
|
|
||||||
|
|
||||||
JavaClass(Decompiler decompiler, ClassNode classNode) {
|
private List<JavaClass> innerClasses = Collections.emptyList();
|
||||||
|
private List<JavaClass> inlinedClasses = Collections.emptyList();
|
||||||
|
private List<JavaField> fields = Collections.emptyList();
|
||||||
|
private List<JavaMethod> methods = Collections.emptyList();
|
||||||
|
private boolean listsLoaded;
|
||||||
|
|
||||||
|
JavaClass(ClassNode classNode, JadxDecompiler decompiler) {
|
||||||
this.decompiler = decompiler;
|
this.decompiler = decompiler;
|
||||||
this.cls = classNode;
|
this.cls = classNode;
|
||||||
|
this.parent = null;
|
||||||
|
}
|
||||||
|
|
||||||
int inClsCount = cls.getInnerClasses().size();
|
/**
|
||||||
if (inClsCount == 0) {
|
* Inner classes constructor
|
||||||
this.innerClasses = Collections.emptyList();
|
*/
|
||||||
|
JavaClass(ClassNode classNode, JavaClass parent) {
|
||||||
|
this.decompiler = null;
|
||||||
|
this.cls = classNode;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return getCodeInfo().getCodeStr();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ICodeInfo getCodeInfo() {
|
||||||
|
ICodeInfo code = load();
|
||||||
|
if (code != null) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return cls.decompile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decompile() {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized ICodeInfo reload() {
|
||||||
|
listsLoaded = false;
|
||||||
|
return cls.reloadCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unload() {
|
||||||
|
listsLoaded = false;
|
||||||
|
cls.unloadCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNoCode() {
|
||||||
|
return cls.contains(AFlag.DONT_GENERATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInner() {
|
||||||
|
return cls.isInner();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized String getSmali() {
|
||||||
|
return cls.getDisassembledCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOwnCodeAnnotation(ICodeAnnotation ann) {
|
||||||
|
if (ann.getAnnType() == ICodeAnnotation.AnnType.CLASS) {
|
||||||
|
return ann.equals(cls);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICodeNodeRef getCodeNodeRef() {
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal API. Not Stable!
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public ClassNode getClassNode() {
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompile class and loads internal lists of fields, methods, etc.
|
||||||
|
* Do nothing if already loaded.
|
||||||
|
*
|
||||||
|
* @return code info if decompilation was executed, null otherwise
|
||||||
|
*/
|
||||||
|
private synchronized @Nullable ICodeInfo load() {
|
||||||
|
if (listsLoaded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
listsLoaded = true;
|
||||||
|
|
||||||
|
ICodeInfo code;
|
||||||
|
if (cls.getState().isProcessComplete()) {
|
||||||
|
// already decompiled -> class internals loaded
|
||||||
|
code = null;
|
||||||
} else {
|
} else {
|
||||||
List<JavaClass> list = new ArrayList<JavaClass>(inClsCount);
|
code = cls.decompile();
|
||||||
|
}
|
||||||
|
|
||||||
|
JadxDecompiler rootDecompiler = getRootDecompiler();
|
||||||
|
int inClsCount = cls.getInnerClasses().size();
|
||||||
|
if (inClsCount != 0) {
|
||||||
|
List<JavaClass> list = new ArrayList<>(inClsCount);
|
||||||
for (ClassNode inner : cls.getInnerClasses()) {
|
for (ClassNode inner : cls.getInnerClasses()) {
|
||||||
list.add(new JavaClass(decompiler, inner));
|
if (!inner.contains(AFlag.DONT_GENERATE)) {
|
||||||
|
JavaClass javaClass = rootDecompiler.convertClassNode(inner);
|
||||||
|
javaClass.load();
|
||||||
|
list.add(javaClass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.innerClasses = Collections.unmodifiableList(list);
|
this.innerClasses = Collections.unmodifiableList(list);
|
||||||
}
|
}
|
||||||
|
int inlinedClsCount = cls.getInlinedClasses().size();
|
||||||
|
if (inlinedClsCount != 0) {
|
||||||
|
List<JavaClass> list = new ArrayList<>(inlinedClsCount);
|
||||||
|
for (ClassNode inner : cls.getInlinedClasses()) {
|
||||||
|
JavaClass javaClass = rootDecompiler.convertClassNode(inner);
|
||||||
|
javaClass.load();
|
||||||
|
list.add(javaClass);
|
||||||
|
}
|
||||||
|
this.inlinedClasses = Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
int fieldsCount = cls.getFields().size();
|
int fieldsCount = cls.getFields().size();
|
||||||
if (fieldsCount == 0) {
|
if (fieldsCount != 0) {
|
||||||
this.fields = Collections.emptyList();
|
List<JavaField> flds = new ArrayList<>(fieldsCount);
|
||||||
} else {
|
|
||||||
List<JavaField> flds = new ArrayList<JavaField>(fieldsCount);
|
|
||||||
for (FieldNode f : cls.getFields()) {
|
for (FieldNode f : cls.getFields()) {
|
||||||
flds.add(new JavaField(f));
|
if (!f.contains(AFlag.DONT_GENERATE)) {
|
||||||
|
flds.add(rootDecompiler.convertFieldNode(f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.fields = Collections.unmodifiableList(flds);
|
this.fields = Collections.unmodifiableList(flds);
|
||||||
}
|
}
|
||||||
|
|
||||||
int methodsCount = cls.getMethods().size();
|
int methodsCount = cls.getMethods().size();
|
||||||
if (methodsCount == 0) {
|
if (methodsCount != 0) {
|
||||||
this.methods = Collections.emptyList();
|
List<JavaMethod> mths = new ArrayList<>(methodsCount);
|
||||||
} else {
|
|
||||||
List<JavaMethod> mths = new ArrayList<JavaMethod>(methodsCount);
|
|
||||||
for (MethodNode m : cls.getMethods()) {
|
for (MethodNode m : cls.getMethods()) {
|
||||||
if (!m.getAccessFlags().isSynthetic()) {
|
if (!m.contains(AFlag.DONT_GENERATE)) {
|
||||||
mths.add(new JavaMethod(m));
|
mths.add(rootDecompiler.convertMethodNode(m));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(mths, new Comparator<JavaMethod>() {
|
mths.sort(Comparator.comparing(JavaMethod::getName));
|
||||||
@Override
|
|
||||||
public int compare(JavaMethod o1, JavaMethod o2) {
|
|
||||||
return o1.getName().compareTo(o2.getName());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.methods = Collections.unmodifiableList(mths);
|
this.methods = Collections.unmodifiableList(mths);
|
||||||
}
|
}
|
||||||
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCode() {
|
JadxDecompiler getRootDecompiler() {
|
||||||
CodeWriter code = cls.getCode();
|
if (parent != null) {
|
||||||
if (code == null) {
|
return parent.getRootDecompiler();
|
||||||
decompiler.processClass(cls);
|
|
||||||
code = cls.getCode();
|
|
||||||
}
|
}
|
||||||
return code != null ? code.toString() : "error processing class";
|
return decompiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ICodeAnnotation getAnnotationAt(int pos) {
|
||||||
|
return getCodeInfo().getCodeMetadata().getAt(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, JavaNode> getUsageMap() {
|
||||||
|
Map<Integer, ICodeAnnotation> map = getCodeInfo().getCodeMetadata().getAsMap();
|
||||||
|
if (map.isEmpty() || decompiler == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
Map<Integer, JavaNode> resultMap = new HashMap<>(map.size());
|
||||||
|
for (Map.Entry<Integer, ICodeAnnotation> entry : map.entrySet()) {
|
||||||
|
int codePosition = entry.getKey();
|
||||||
|
ICodeAnnotation obj = entry.getValue();
|
||||||
|
if (obj instanceof ICodeNodeRef) {
|
||||||
|
JavaNode node = getRootDecompiler().getJavaNodeByRef((ICodeNodeRef) obj);
|
||||||
|
if (node != null) {
|
||||||
|
resultMap.put(codePosition, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getUsePlacesFor(ICodeInfo codeInfo, JavaNode javaNode) {
|
||||||
|
if (!codeInfo.hasMetadata()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
codeInfo.getCodeMetadata().searchDown(0, (pos, ann) -> {
|
||||||
|
if (javaNode.isOwnCodeAnnotation(ann)) {
|
||||||
|
result.add(pos);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JavaNode> getUseIn() {
|
||||||
|
return getRootDecompiler().convertNodes(cls.getUseIn());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSourceLine(int decompiledLine) {
|
||||||
|
return getCodeInfo().getCodeMetadata().getLineMapping().get(decompiledLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return cls.getShortName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
return cls.getFullName();
|
return cls.getFullName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getShortName() {
|
public String getRawName() {
|
||||||
return cls.getShortName();
|
return cls.getRawName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPackage() {
|
public String getPackage() {
|
||||||
return cls.getPackage();
|
return cls.getPackage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getDeclaringClass() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JavaClass getOriginalTopParentClass() {
|
||||||
|
return parent == null ? this : parent.getOriginalTopParentClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return top parent class which contains code of this class.
|
||||||
|
* Code parent can be different from original parent after move or inline
|
||||||
|
*
|
||||||
|
* @return this if already a top class
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public JavaClass getTopParentClass() {
|
||||||
|
JavaClass codeParent = getCodeParent();
|
||||||
|
return codeParent == null ? this : codeParent.getTopParentClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return parent class which contains code of this class.
|
||||||
|
* Code parent can be different for original parent after move or inline
|
||||||
|
*/
|
||||||
|
public @Nullable JavaClass getCodeParent() {
|
||||||
|
AnonymousClassAttr anonymousClsAttr = cls.get(AType.ANONYMOUS_CLASS);
|
||||||
|
if (anonymousClsAttr != null) {
|
||||||
|
// moved to usage class
|
||||||
|
return getRootDecompiler().convertClassNode(anonymousClsAttr.getOuterCls());
|
||||||
|
}
|
||||||
|
InlinedAttr inlinedAttr = cls.get(AType.INLINED);
|
||||||
|
if (inlinedAttr != null) {
|
||||||
|
return getRootDecompiler().convertClassNode(inlinedAttr.getInlineCls());
|
||||||
|
}
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
public AccessInfo getAccessInfo() {
|
public AccessInfo getAccessInfo() {
|
||||||
return cls.getAccessFlags();
|
return cls.getAccessFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<JavaClass> getInnerClasses() {
|
public List<JavaClass> getInnerClasses() {
|
||||||
|
load();
|
||||||
return innerClasses;
|
return innerClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<JavaClass> getInlinedClasses() {
|
||||||
|
load();
|
||||||
|
return inlinedClasses;
|
||||||
|
}
|
||||||
|
|
||||||
public List<JavaField> getFields() {
|
public List<JavaField> getFields() {
|
||||||
|
load();
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<JavaMethod> getMethods() {
|
public List<JavaMethod> getMethods() {
|
||||||
|
load();
|
||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public JavaMethod searchMethodByShortId(String shortId) {
|
||||||
|
MethodNode methodNode = cls.searchMethodByShortId(shortId);
|
||||||
|
if (methodNode == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getRootDecompiler().convertMethodNode(methodNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JavaClass> getDependencies() {
|
||||||
|
JadxDecompiler d = getRootDecompiler();
|
||||||
|
return ListUtils.map(cls.getDependencies(), d::convertClassNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalDepsCount() {
|
||||||
|
return cls.getTotalDepsCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAlias() {
|
||||||
|
cls.removeAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefPos() {
|
||||||
|
return cls.getDefPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return this == o || o instanceof JavaClass && cls.equals(((JavaClass) o).cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return cls.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getFullName();
|
return getFullName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDecompiledLine() {
|
|
||||||
return cls.getDecompiledLine();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,105 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
|
|
||||||
public final class JavaField {
|
public final class JavaField implements JavaNode {
|
||||||
|
|
||||||
private final FieldNode field;
|
private final FieldNode field;
|
||||||
|
private final JavaClass parent;
|
||||||
|
|
||||||
public JavaField(FieldNode f) {
|
JavaField(JavaClass cls, FieldNode f) {
|
||||||
this.field = f;
|
this.field = f;
|
||||||
|
this.parent = cls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
return field.getAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullName() {
|
||||||
|
return parent.getFullName() + '.' + getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRawName() {
|
||||||
return field.getName();
|
return field.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getDeclaringClass() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getTopParentClass() {
|
||||||
|
return parent.getTopParentClass();
|
||||||
|
}
|
||||||
|
|
||||||
public AccessInfo getAccessFlags() {
|
public AccessInfo getAccessFlags() {
|
||||||
return field.getAccessFlags();
|
return field.getAccessFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArgType getType() {
|
public ArgType getType() {
|
||||||
return field.getType();
|
return ArgType.tryToResolveClassAlias(field.root(), field.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDecompiledLine() {
|
@Override
|
||||||
return field.getDecompiledLine();
|
public int getDefPos() {
|
||||||
|
return field.getDefPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JavaNode> getUseIn() {
|
||||||
|
return getDeclaringClass().getRootDecompiler().convertNodes(field.getUseIn());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAlias() {
|
||||||
|
this.field.getFieldInfo().removeAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOwnCodeAnnotation(ICodeAnnotation ann) {
|
||||||
|
if (ann.getAnnType() == ICodeAnnotation.AnnType.FIELD) {
|
||||||
|
return ann.equals(field);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICodeNodeRef getCodeNodeRef() {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal API. Not Stable!
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public FieldNode getFieldNode() {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return field.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return this == o || o instanceof JavaField && field.equals(((JavaField) o).field);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return field.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,52 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
|
import jadx.core.dex.attributes.AType;
|
||||||
|
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.info.MethodInfo;
|
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
import jadx.core.utils.Utils;
|
||||||
|
|
||||||
import java.util.List;
|
public final class JavaMethod implements JavaNode {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JavaMethod.class);
|
||||||
|
|
||||||
public final class JavaMethod {
|
|
||||||
private final MethodNode mth;
|
private final MethodNode mth;
|
||||||
|
private final JavaClass parent;
|
||||||
|
|
||||||
public JavaMethod(MethodNode m) {
|
JavaMethod(JavaClass cls, MethodNode m) {
|
||||||
|
this.parent = cls;
|
||||||
this.mth = m;
|
this.mth = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
MethodInfo mi = mth.getMethodInfo();
|
return mth.getAlias();
|
||||||
if (mi.isConstructor()) {
|
}
|
||||||
return mth.getParentClass().getShortName();
|
|
||||||
} else if (mi.isClassInit()) {
|
@Override
|
||||||
return "static";
|
public String getFullName() {
|
||||||
}
|
return mth.getMethodInfo().getFullName();
|
||||||
return mi.getName();
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getDeclaringClass() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getTopParentClass() {
|
||||||
|
return parent.getTopParentClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccessInfo getAccessFlags() {
|
public AccessInfo getAccessFlags() {
|
||||||
@@ -29,11 +54,41 @@ public final class JavaMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ArgType> getArguments() {
|
public List<ArgType> getArguments() {
|
||||||
return mth.getMethodInfo().getArgumentsTypes();
|
List<ArgType> infoArgTypes = mth.getMethodInfo().getArgumentsTypes();
|
||||||
|
if (infoArgTypes.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<ArgType> arguments = mth.getArgTypes();
|
||||||
|
return Utils.collectionMap(arguments,
|
||||||
|
type -> ArgType.tryToResolveClassAlias(mth.root(), type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArgType getReturnType() {
|
public ArgType getReturnType() {
|
||||||
return mth.getReturnType();
|
ArgType retType = mth.getReturnType();
|
||||||
|
return ArgType.tryToResolveClassAlias(mth.root(), retType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JavaNode> getUseIn() {
|
||||||
|
return getDeclaringClass().getRootDecompiler().convertNodes(mth.getUseIn());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JavaMethod> getOverrideRelatedMethods() {
|
||||||
|
MethodOverrideAttr ovrdAttr = mth.get(AType.METHOD_OVERRIDE);
|
||||||
|
if (ovrdAttr == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
JadxDecompiler decompiler = getDeclaringClass().getRootDecompiler();
|
||||||
|
return ovrdAttr.getRelatedMthNodes().stream()
|
||||||
|
.map(m -> {
|
||||||
|
JavaMethod javaMth = decompiler.convertMethodNode(m);
|
||||||
|
if (javaMth == null) {
|
||||||
|
LOG.warn("Failed convert to java method: {}", m);
|
||||||
|
}
|
||||||
|
return javaMth;
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConstructor() {
|
public boolean isConstructor() {
|
||||||
@@ -44,7 +99,49 @@ public final class JavaMethod {
|
|||||||
return mth.getMethodInfo().isClassInit();
|
return mth.getMethodInfo().isClassInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDecompiledLine() {
|
@Override
|
||||||
return mth.getDecompiledLine();
|
public int getDefPos() {
|
||||||
|
return mth.getDefPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAlias() {
|
||||||
|
this.mth.getMethodInfo().removeAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOwnCodeAnnotation(ICodeAnnotation ann) {
|
||||||
|
if (ann.getAnnType() == ICodeAnnotation.AnnType.METHOD) {
|
||||||
|
return ann.equals(mth);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICodeNodeRef getCodeNodeRef() {
|
||||||
|
return mth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal API. Not Stable!
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public MethodNode getMethodNode() {
|
||||||
|
return mth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return mth.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return this == o || o instanceof JavaMethod && mth.equals(((JavaMethod) o).mth);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return mth.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
|
|
||||||
|
public interface JavaNode {
|
||||||
|
|
||||||
|
ICodeNodeRef getCodeNodeRef();
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
String getFullName();
|
||||||
|
|
||||||
|
JavaClass getDeclaringClass();
|
||||||
|
|
||||||
|
JavaClass getTopParentClass();
|
||||||
|
|
||||||
|
int getDefPos();
|
||||||
|
|
||||||
|
List<JavaNode> getUseIn();
|
||||||
|
|
||||||
|
void removeAlias();
|
||||||
|
|
||||||
|
boolean isOwnCodeAnnotation(ICodeAnnotation ann);
|
||||||
|
}
|
||||||
@@ -1,45 +1,149 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public final class JavaPackage implements Comparable<JavaPackage> {
|
import org.jetbrains.annotations.ApiStatus.Internal;
|
||||||
private final String name;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
|
import jadx.core.dex.info.PackageInfo;
|
||||||
|
import jadx.core.dex.nodes.PackageNode;
|
||||||
|
|
||||||
|
public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
||||||
|
private final PackageNode pkgNode;
|
||||||
private final List<JavaClass> classes;
|
private final List<JavaClass> classes;
|
||||||
|
private final List<JavaPackage> subPkgs;
|
||||||
|
|
||||||
JavaPackage(String name, List<JavaClass> classes) {
|
JavaPackage(PackageNode pkgNode, List<JavaClass> classes, List<JavaPackage> subPkgs) {
|
||||||
this.name = name;
|
this.pkgNode = pkgNode;
|
||||||
this.classes = classes;
|
this.classes = classes;
|
||||||
|
this.subPkgs = subPkgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return pkgNode.getAliasPkgInfo().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullName() {
|
||||||
|
return pkgNode.getAliasPkgInfo().getFullName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRawName() {
|
||||||
|
return pkgNode.getPkgInfo().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRawFullName() {
|
||||||
|
return pkgNode.getPkgInfo().getFullName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JavaPackage> getSubPackages() {
|
||||||
|
return subPkgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<JavaClass> getClasses() {
|
public List<JavaClass> getClasses() {
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRoot() {
|
||||||
|
return pkgNode.isRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return pkgNode.isLeaf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDefault() {
|
||||||
|
return getFullName().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rename(String alias) {
|
||||||
|
pkgNode.rename(alias);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(JavaPackage o) {
|
public void removeAlias() {
|
||||||
return name.compareTo(o.name);
|
pkgNode.removeAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParentRenamed() {
|
||||||
|
PackageInfo parent = pkgNode.getPkgInfo().getParentPkg();
|
||||||
|
PackageInfo aliasParent = pkgNode.getAliasPkgInfo().getParentPkg();
|
||||||
|
return !Objects.equals(parent, aliasParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICodeNodeRef getCodeNodeRef() {
|
||||||
|
return pkgNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public PackageNode getPkgNode() {
|
||||||
|
return pkgNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getDeclaringClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getTopParentClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefPos() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JavaNode> getUseIn() {
|
||||||
|
List<JavaNode> list = new ArrayList<>();
|
||||||
|
addUseIn(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUseIn(List<JavaNode> list) {
|
||||||
|
list.addAll(classes);
|
||||||
|
for (JavaPackage subPkg : subPkgs) {
|
||||||
|
subPkg.addUseIn(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOwnCodeAnnotation(ICodeAnnotation ann) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NotNull JavaPackage o) {
|
||||||
|
return pkgNode.compareTo(o.pkgNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
JavaPackage that = (JavaPackage) o;
|
JavaPackage that = (JavaPackage) o;
|
||||||
if (!name.equals(that.name)) return false;
|
return pkgNode.equals(that.pkgNode);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return name.hashCode();
|
return pkgNode.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return pkgNode.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
|
import jadx.api.metadata.annotations.VarNode;
|
||||||
|
import jadx.api.metadata.annotations.VarRef;
|
||||||
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
|
|
||||||
|
public class JavaVariable implements JavaNode {
|
||||||
|
private final JavaMethod mth;
|
||||||
|
private final VarNode varNode;
|
||||||
|
|
||||||
|
public JavaVariable(JavaMethod mth, VarNode varNode) {
|
||||||
|
this.mth = mth;
|
||||||
|
this.varNode = varNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JavaMethod getMth() {
|
||||||
|
return mth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getReg() {
|
||||||
|
return varNode.getReg();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSsa() {
|
||||||
|
return varNode.getSsa();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getName() {
|
||||||
|
return varNode.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICodeNodeRef getCodeNodeRef() {
|
||||||
|
return varNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public VarNode getVarNode() {
|
||||||
|
return varNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullName() {
|
||||||
|
return varNode.getType() + " " + varNode.getName() + " (r" + varNode.getReg() + "v" + varNode.getSsa() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgType getType() {
|
||||||
|
return ArgType.tryToResolveClassAlias(mth.getMethodNode().root(), varNode.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getDeclaringClass() {
|
||||||
|
return mth.getDeclaringClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getTopParentClass() {
|
||||||
|
return mth.getTopParentClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefPos() {
|
||||||
|
return varNode.getDefPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JavaNode> getUseIn() {
|
||||||
|
return Collections.singletonList(mth);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAlias() {
|
||||||
|
varNode.setName(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOwnCodeAnnotation(ICodeAnnotation ann) {
|
||||||
|
if (ann.getAnnType() == ICodeAnnotation.AnnType.VAR_REF) {
|
||||||
|
VarRef varRef = (VarRef) ann;
|
||||||
|
return varRef.getRefPos() == getDefPos();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return varNode.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof JavaVariable)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return varNode.equals(((JavaVariable) o).varNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import jadx.api.plugins.utils.ZipSecurity;
|
||||||
|
import jadx.core.xmlgen.ResContainer;
|
||||||
|
import jadx.core.xmlgen.entry.ResourceEntry;
|
||||||
|
|
||||||
|
public class ResourceFile {
|
||||||
|
|
||||||
|
public static final class ZipRef {
|
||||||
|
private final File zipFile;
|
||||||
|
private final String entryName;
|
||||||
|
|
||||||
|
public ZipRef(File zipFile, String entryName) {
|
||||||
|
this.zipFile = zipFile;
|
||||||
|
this.entryName = entryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getZipFile() {
|
||||||
|
return zipFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEntryName() {
|
||||||
|
return entryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ZipRef{" + zipFile + ", '" + entryName + "'}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final JadxDecompiler decompiler;
|
||||||
|
private final String name;
|
||||||
|
private final ResourceType type;
|
||||||
|
private ZipRef zipRef;
|
||||||
|
private String deobfName;
|
||||||
|
|
||||||
|
public static ResourceFile createResourceFile(JadxDecompiler decompiler, File file, ResourceType type) {
|
||||||
|
return new ResourceFile(decompiler, file.getAbsolutePath(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResourceFile createResourceFile(JadxDecompiler decompiler, String name, ResourceType type) {
|
||||||
|
if (!ZipSecurity.isValidZipEntryName(name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ResourceFile(decompiler, name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ResourceFile(JadxDecompiler decompiler, String name, ResourceType type) {
|
||||||
|
this.decompiler = decompiler;
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOriginalName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeobfName() {
|
||||||
|
return deobfName != null ? deobfName : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResContainer loadContent() {
|
||||||
|
return ResourcesLoader.loadContent(decompiler, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setZipRef(ZipRef zipRef) {
|
||||||
|
this.zipRef = zipRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setAlias(ResourceEntry ri) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("res/").append(ri.getTypeName()).append(ri.getConfig());
|
||||||
|
sb.append("/").append(ri.getKeyName());
|
||||||
|
int lastDot = name.lastIndexOf('.');
|
||||||
|
if (lastDot != -1) {
|
||||||
|
sb.append(name.substring(lastDot));
|
||||||
|
}
|
||||||
|
String alias = sb.toString();
|
||||||
|
if (!alias.equals(name)) {
|
||||||
|
deobfName = alias;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZipRef getZipRef() {
|
||||||
|
return zipRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ResourceFile{name='" + name + '\'' + ", type=" + type + '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import jadx.core.xmlgen.ResContainer;
|
||||||
|
|
||||||
|
public class ResourceFileContent extends ResourceFile {
|
||||||
|
private final ICodeInfo content;
|
||||||
|
|
||||||
|
public ResourceFileContent(String name, ResourceType type, ICodeInfo content) {
|
||||||
|
super(null, name, type);
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResContainer loadContent() {
|
||||||
|
return ResContainer.textResource(getDeobfName(), content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
|
public enum ResourceType {
|
||||||
|
CODE(".dex", ".jar", ".class"),
|
||||||
|
XML(".xml"),
|
||||||
|
ARSC(".arsc"),
|
||||||
|
FONT(".ttf", ".otf"),
|
||||||
|
IMG(".png", ".gif", ".jpg"),
|
||||||
|
MEDIA(".mp3", ".wav"),
|
||||||
|
LIB(".so"),
|
||||||
|
MANIFEST,
|
||||||
|
UNKNOWN;
|
||||||
|
|
||||||
|
private final String[] exts;
|
||||||
|
|
||||||
|
ResourceType(String... exts) {
|
||||||
|
this.exts = exts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getExts() {
|
||||||
|
return exts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<String, ResourceType> EXT_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (ResourceType type : ResourceType.values()) {
|
||||||
|
for (String ext : type.getExts()) {
|
||||||
|
ResourceType prev = EXT_MAP.put(ext, type);
|
||||||
|
if (prev != null) {
|
||||||
|
throw new JadxRuntimeException("Duplicate extension in ResourceType: " + ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResourceType getFileType(String fileName) {
|
||||||
|
if (fileName.endsWith("/resources.pb")) {
|
||||||
|
return ARSC;
|
||||||
|
}
|
||||||
|
int dot = fileName.lastIndexOf('.');
|
||||||
|
if (dot != -1) {
|
||||||
|
String ext = fileName.substring(dot).toLowerCase(Locale.ROOT);
|
||||||
|
ResourceType resType = EXT_MAP.get(ext);
|
||||||
|
if (resType != null) {
|
||||||
|
if (resType == XML && fileName.equals("AndroidManifest.xml")) {
|
||||||
|
return MANIFEST;
|
||||||
|
}
|
||||||
|
return resType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.ResourceFile.ZipRef;
|
||||||
|
import jadx.api.impl.SimpleCodeInfo;
|
||||||
|
import jadx.api.plugins.CustomResourcesLoader;
|
||||||
|
import jadx.api.plugins.utils.ZipSecurity;
|
||||||
|
import jadx.core.dex.nodes.RootNode;
|
||||||
|
import jadx.core.utils.Utils;
|
||||||
|
import jadx.core.utils.android.Res9patchStreamDecoder;
|
||||||
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
|
import jadx.core.utils.files.FileUtils;
|
||||||
|
import jadx.core.xmlgen.ResContainer;
|
||||||
|
import jadx.core.xmlgen.ResProtoParser;
|
||||||
|
import jadx.core.xmlgen.ResTableParser;
|
||||||
|
|
||||||
|
import static jadx.core.utils.files.FileUtils.READ_BUFFER_SIZE;
|
||||||
|
import static jadx.core.utils.files.FileUtils.copyStream;
|
||||||
|
|
||||||
|
// TODO: move to core package
|
||||||
|
public final class ResourcesLoader {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ResourcesLoader.class);
|
||||||
|
|
||||||
|
private final JadxDecompiler jadxRef;
|
||||||
|
|
||||||
|
ResourcesLoader(JadxDecompiler jadxRef) {
|
||||||
|
this.jadxRef = jadxRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ResourceFile> load() {
|
||||||
|
List<File> inputFiles = jadxRef.getArgs().getInputFiles();
|
||||||
|
List<ResourceFile> list = new ArrayList<>(inputFiles.size());
|
||||||
|
for (File file : inputFiles) {
|
||||||
|
loadFile(list, file);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ResourceDecoder<T> {
|
||||||
|
T decode(long size, InputStream is) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T decodeStream(ResourceFile rf, ResourceDecoder<T> decoder) throws JadxException {
|
||||||
|
try {
|
||||||
|
ZipRef zipRef = rf.getZipRef();
|
||||||
|
if (zipRef == null) {
|
||||||
|
File file = new File(rf.getOriginalName());
|
||||||
|
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
|
||||||
|
return decoder.decode(file.length(), inputStream);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try (ZipFile zipFile = new ZipFile(zipRef.getZipFile())) {
|
||||||
|
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
|
||||||
|
if (entry == null) {
|
||||||
|
throw new IOException("Zip entry not found: " + zipRef);
|
||||||
|
}
|
||||||
|
if (!ZipSecurity.isValidZipEntry(entry)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try (InputStream inputStream = ZipSecurity.getInputStreamForEntry(zipFile, entry)) {
|
||||||
|
return decoder.decode(entry.getSize(), inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new JadxException("Error decode: " + rf.getDeobfName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf) {
|
||||||
|
try {
|
||||||
|
return decodeStream(rf, (size, is) -> loadContent(jadxRef, rf, is));
|
||||||
|
} catch (JadxException e) {
|
||||||
|
LOG.error("Decode error", e);
|
||||||
|
ICodeWriter cw = jadxRef.getRoot().makeCodeWriter();
|
||||||
|
cw.add("Error decode ").add(rf.getType().toString().toLowerCase());
|
||||||
|
Utils.appendStackTrace(cw, e.getCause());
|
||||||
|
return ResContainer.textResource(rf.getDeobfName(), cw.finish());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf,
|
||||||
|
InputStream inputStream) throws IOException {
|
||||||
|
RootNode root = jadxRef.getRoot();
|
||||||
|
switch (rf.getType()) {
|
||||||
|
case MANIFEST:
|
||||||
|
case XML: {
|
||||||
|
ICodeInfo content;
|
||||||
|
if (root.isProto()) {
|
||||||
|
content = jadxRef.getProtoXmlParser().parse(inputStream);
|
||||||
|
} else {
|
||||||
|
content = jadxRef.getBinaryXmlParser().parse(inputStream);
|
||||||
|
}
|
||||||
|
return ResContainer.textResource(rf.getDeobfName(), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
case ARSC:
|
||||||
|
if (root.isProto()) {
|
||||||
|
return new ResProtoParser(root).decodeFiles(inputStream);
|
||||||
|
} else {
|
||||||
|
return new ResTableParser(root).decodeFiles(inputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
case IMG:
|
||||||
|
return decodeImage(rf, inputStream);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ResContainer.resourceFileLink(rf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResContainer decodeImage(ResourceFile rf, InputStream inputStream) {
|
||||||
|
String name = rf.getOriginalName();
|
||||||
|
if (name.endsWith(".9.png")) {
|
||||||
|
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
|
||||||
|
Res9patchStreamDecoder decoder = new Res9patchStreamDecoder();
|
||||||
|
if (decoder.decode(inputStream, os)) {
|
||||||
|
return ResContainer.decodedData(rf.getDeobfName(), os.toByteArray());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Failed to decode 9-patch png image, path: {}", name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ResContainer.resourceFileLink(rf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFile(List<ResourceFile> list, File file) {
|
||||||
|
if (file == null || file.isDirectory()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to load the resources with a custom loader first
|
||||||
|
for (CustomResourcesLoader loader : jadxRef.getCustomResourcesLoaders()) {
|
||||||
|
if (loader.load(this, list, file)) {
|
||||||
|
LOG.debug("Custom loader used for {}", file.getAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no custom decoder was able to decode the resources, use the default decoder
|
||||||
|
defaultLoadFile(list, file, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defaultLoadFile(List<ResourceFile> list, File file, String subDir) {
|
||||||
|
if (FileUtils.isZipFile(file)) {
|
||||||
|
ZipSecurity.visitZipEntries(file, (zipFile, entry) -> {
|
||||||
|
addEntry(list, file, entry, subDir);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ResourceType type = ResourceType.getFileType(file.getAbsolutePath());
|
||||||
|
list.add(ResourceFile.createResourceFile(jadxRef, file, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntry(List<ResourceFile> list, File zipFile, ZipEntry entry, String subDir) {
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String name = entry.getName();
|
||||||
|
ResourceType type = ResourceType.getFileType(name);
|
||||||
|
ResourceFile rf = ResourceFile.createResourceFile(jadxRef, subDir + name, type);
|
||||||
|
if (rf != null) {
|
||||||
|
rf.setZipRef(new ZipRef(zipFile, name));
|
||||||
|
list.add(rf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ICodeInfo loadToCodeWriter(InputStream is) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(READ_BUFFER_SIZE);
|
||||||
|
copyStream(is, baos);
|
||||||
|
return new SimpleCodeInfo(baos.toString("UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package jadx.api.args;
|
||||||
|
|
||||||
|
public enum GeneratedRenamesMappingFileMode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load if found, don't save (default)
|
||||||
|
*/
|
||||||
|
READ,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load if found, save only if new (don't overwrite)
|
||||||
|
*/
|
||||||
|
READ_OR_SAVE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't load, always save
|
||||||
|
*/
|
||||||
|
OVERWRITE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't load and don't save
|
||||||
|
*/
|
||||||
|
IGNORE;
|
||||||
|
|
||||||
|
public static GeneratedRenamesMappingFileMode getDefault() {
|
||||||
|
return READ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldRead() {
|
||||||
|
return this == READ || this == READ_OR_SAVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldWrite() {
|
||||||
|
return this == READ_OR_SAVE || this == OVERWRITE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package jadx.api.args;
|
||||||
|
|
||||||
|
public enum IntegerFormat {
|
||||||
|
AUTO,
|
||||||
|
DECIMAL,
|
||||||
|
HEXADECIMAL;
|
||||||
|
|
||||||
|
public boolean isHexadecimal() {
|
||||||
|
return this == HEXADECIMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package jadx.api.args;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resources original name source (for deobfuscation)
|
||||||
|
*/
|
||||||
|
public enum ResourceNameSource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically select best name (default)
|
||||||
|
*/
|
||||||
|
AUTO,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force use resources provided names
|
||||||
|
*/
|
||||||
|
RESOURCES,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force use resources names from R class
|
||||||
|
*/
|
||||||
|
CODE,
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package jadx.api.args;
|
||||||
|
|
||||||
|
public enum UserRenamesMappingsMode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just read, user can save manually (default)
|
||||||
|
*/
|
||||||
|
READ,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read and autosave after every change
|
||||||
|
*/
|
||||||
|
READ_AND_AUTOSAVE_EVERY_CHANGE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read and autosave before exiting the app or closing the project
|
||||||
|
*/
|
||||||
|
READ_AND_AUTOSAVE_BEFORE_CLOSING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't load and don't save
|
||||||
|
*/
|
||||||
|
IGNORE;
|
||||||
|
|
||||||
|
public static UserRenamesMappingsMode getDefault() {
|
||||||
|
return READ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldRead() {
|
||||||
|
return this != IGNORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldWrite() {
|
||||||
|
return this == READ_AND_AUTOSAVE_EVERY_CHANGE || this == READ_AND_AUTOSAVE_BEFORE_CLOSING;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package jadx.api.data;
|
||||||
|
|
||||||
|
public enum CodeRefType {
|
||||||
|
MTH_ARG,
|
||||||
|
VAR,
|
||||||
|
CATCH,
|
||||||
|
INSN,
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package jadx.api.data;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface ICodeComment extends Comparable<ICodeComment> {
|
||||||
|
|
||||||
|
IJavaNodeRef getNodeRef();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
IJavaCodeRef getCodeRef();
|
||||||
|
|
||||||
|
String getComment();
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package jadx.api.data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ICodeData {
|
||||||
|
|
||||||
|
List<ICodeComment> getComments();
|
||||||
|
|
||||||
|
List<ICodeRename> getRenames();
|
||||||
|
|
||||||
|
boolean isEmpty();
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package jadx.api.data;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface ICodeRename extends Comparable<ICodeRename> {
|
||||||
|
|
||||||
|
IJavaNodeRef getNodeRef();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
IJavaCodeRef getCodeRef();
|
||||||
|
|
||||||
|
String getNewName();
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package jadx.api.data;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public interface IJavaCodeRef extends Comparable<IJavaCodeRef> {
|
||||||
|
|
||||||
|
CodeRefType getAttachType();
|
||||||
|
|
||||||
|
int getIndex();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default int compareTo(@NotNull IJavaCodeRef o) {
|
||||||
|
return Integer.compare(getIndex(), o.getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package jadx.api.data;
|
||||||
|
|
||||||
|
public interface IJavaNodeRef extends Comparable<IJavaNodeRef> {
|
||||||
|
|
||||||
|
enum RefType {
|
||||||
|
CLASS, FIELD, METHOD, PKG
|
||||||
|
}
|
||||||
|
|
||||||
|
RefType getType();
|
||||||
|
|
||||||
|
String getDeclaringClass();
|
||||||
|
|
||||||
|
String getShortId();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package jadx.api.data;
|
||||||
|
|
||||||
|
public interface IRenameNode {
|
||||||
|
|
||||||
|
void rename(String newName);
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package jadx.api.data.impl;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import jadx.api.data.ICodeComment;
|
||||||
|
import jadx.api.data.IJavaCodeRef;
|
||||||
|
import jadx.api.data.IJavaNodeRef;
|
||||||
|
|
||||||
|
public class JadxCodeComment implements ICodeComment {
|
||||||
|
|
||||||
|
private IJavaNodeRef nodeRef;
|
||||||
|
@Nullable
|
||||||
|
private IJavaCodeRef codeRef;
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
public JadxCodeComment(IJavaNodeRef nodeRef, String comment) {
|
||||||
|
this(nodeRef, null, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JadxCodeComment(IJavaNodeRef nodeRef, @Nullable IJavaCodeRef codeRef, String comment) {
|
||||||
|
this.nodeRef = nodeRef;
|
||||||
|
this.codeRef = codeRef;
|
||||||
|
this.comment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JadxCodeComment() {
|
||||||
|
// for json deserialization
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IJavaNodeRef getNodeRef() {
|
||||||
|
return nodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeRef(IJavaNodeRef nodeRef) {
|
||||||
|
this.nodeRef = nodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public IJavaCodeRef getCodeRef() {
|
||||||
|
return codeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodeRef(@Nullable IJavaCodeRef codeRef) {
|
||||||
|
this.codeRef = codeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComment(String comment) {
|
||||||
|
this.comment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NotNull ICodeComment other) {
|
||||||
|
int cmpNodeRef = this.getNodeRef().compareTo(other.getNodeRef());
|
||||||
|
if (cmpNodeRef != 0) {
|
||||||
|
return cmpNodeRef;
|
||||||
|
}
|
||||||
|
if (this.getCodeRef() != null && other.getCodeRef() != null) {
|
||||||
|
return this.getCodeRef().compareTo(other.getCodeRef());
|
||||||
|
}
|
||||||
|
return this.getComment().compareTo(other.getComment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "JadxCodeComment{" + nodeRef
|
||||||
|
+ ", ref=" + codeRef
|
||||||
|
+ ", comment='" + comment + '\''
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package jadx.api.data.impl;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jadx.api.data.ICodeComment;
|
||||||
|
import jadx.api.data.ICodeData;
|
||||||
|
import jadx.api.data.ICodeRename;
|
||||||
|
|
||||||
|
public class JadxCodeData implements ICodeData {
|
||||||
|
private List<ICodeComment> comments = Collections.emptyList();
|
||||||
|
private List<ICodeRename> renames = Collections.emptyList();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ICodeComment> getComments() {
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComments(List<ICodeComment> comments) {
|
||||||
|
this.comments = comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ICodeRename> getRenames() {
|
||||||
|
return renames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenames(List<ICodeRename> renames) {
|
||||||
|
this.renames = renames;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return comments.isEmpty() && renames.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user