Compare commits
1683 Commits
v0.6.0
..
issue-1973
| 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 |
@@ -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>
|
||||||
-24
@@ -1,24 +0,0 @@
|
|||||||
language: java
|
|
||||||
jdk:
|
|
||||||
- oraclejdk8
|
|
||||||
- oraclejdk7
|
|
||||||
- openjdk6
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- chmod +x gradlew
|
|
||||||
|
|
||||||
script:
|
|
||||||
- TERM=dumb ./gradlew clean build dist
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- TERM=dumb ./gradlew jacocoTestReport coveralls
|
|
||||||
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.gradle
|
|
||||||
|
|
||||||
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`
|
||||||
@@ -144,7 +144,8 @@ THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
Jadx-gui components
|
Jadx-gui components
|
||||||
===================
|
===================
|
||||||
|
|
||||||
RSyntaxTextArea library licensed under modified BSD license:
|
RSyntaxTextArea library (https://github.com/bobbylight/RSyntaxTextArea)
|
||||||
|
licensed under modified BSD license:
|
||||||
|
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
Copyright (c) 2012, Robert Futrell
|
Copyright (c) 2012, Robert Futrell
|
||||||
@@ -174,8 +175,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
|
|
||||||
|
|
||||||
Icons copied from several places:
|
Concurrent Trees (https://code.google.com/p/concurrent-trees/)
|
||||||
- Eclipse Project (JDT UI) - licensed under EPL v1.0 (http://www.eclipse.org/legal/epl-v10.html)
|
licenced under Apache License 2.0:
|
||||||
- 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/)
|
|
||||||
|
*******************************************************************************
|
||||||
|
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/
|
JFontChooser Component - http://sourceforge.jp/projects/jfontchooser/
|
||||||
|
|
||||||
|
Icons copied from several places:
|
||||||
|
- 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/)
|
||||||
|
|||||||
@@ -1,89 +1,201 @@
|
|||||||
|
<img src="https://raw.githubusercontent.com/skylot/jadx/master/jadx-gui/src/main/resources/logos/jadx-logo.png" width="64" align="left" />
|
||||||
|
|
||||||
## JADX
|
## JADX
|
||||||
|
|
||||||
[](https://travis-ci.org/skylot/jadx)
|

|
||||||
[](https://drone.io/github.com/skylot/jadx/latest)
|

|
||||||
[](https://coveralls.io/r/skylot/jadx)
|

|
||||||
[](https://scan.coverity.com/projects/2166)
|

|
||||||
|

|
||||||
|
[](https://search.maven.org/search?q=g:io.github.skylot%20AND%20jadx)
|
||||||
[](http://www.apache.org/licenses/LICENSE-2.0.html)
|
[](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
|
||||||
|
|
||||||

|
: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
|
||||||
|
|
||||||
### Downloads
|
**Main features:**
|
||||||
- [unstable](https://drone.io/github.com/skylot/jadx/files)
|
- decompile Dalvik bytecode to java classes from APK, dex, aar, aab and zip files
|
||||||
- from [github](https://github.com/skylot/jadx/releases)
|
- decode `AndroidManifest.xml` and other resources from `resources.arsc`
|
||||||
- from [sourceforge](http://sourceforge.net/projects/jadx/files/)
|
- deobfuscator included
|
||||||
|
|
||||||
|
**jadx-gui features:**
|
||||||
|
- view decompiled code with highlighted syntax
|
||||||
|
- jump to declaration
|
||||||
|
- find usage
|
||||||
|
- full text search
|
||||||
|
- smali debugger, check [wiki page](https://github.com/skylot/jadx/wiki/Smali-debugger) for setup and usage
|
||||||
|
|
||||||
### Building from source
|
Jadx-gui key bindings can be found [here](https://github.com/skylot/jadx/wiki/JADX-GUI-Key-bindings)
|
||||||
git clone https://github.com/skylot/jadx.git
|
|
||||||
cd jadx
|
See these features in action here: [jadx-gui features overview](https://github.com/skylot/jadx/wiki/jadx-gui-features-overview)
|
||||||
./gradlew dist
|
|
||||||
|
<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
|
||||||
|
```
|
||||||
|
|
||||||
(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, .jar or .class)
|
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
|
||||||
-r, --no-res - do not decode resources
|
-r, --no-res - do not decode resources
|
||||||
-s, --no-src - do not decompile source code
|
-s, --no-src - do not decompile source code
|
||||||
--show-bad-code - show inconsistent code (incorrectly decompiled)
|
--single-class - decompile a single class, full name, raw or alias
|
||||||
--cfg - save methods control flow graph to dot file
|
--single-class-output - file or dir for write if decompile a single class
|
||||||
--raw-cfg - save methods control flow graph (use raw instructions)
|
--output-format - can be 'java' or 'json', default: java
|
||||||
-v, --verbose - verbose output
|
-e, --export-gradle - save as android gradle project
|
||||||
--deobf - activate deobfuscation
|
-j, --threads-count - processing threads count, default: 4
|
||||||
--deobf-min - min length of name
|
-m, --decompilation-mode - code output mode:
|
||||||
--deobf-max - max length of name
|
'auto' - trying best options (default)
|
||||||
--deobf-rewrite-cfg - force to save deobfuscation map
|
'restructure' - restore code structure (normal java code)
|
||||||
-h, --help - print this help
|
'simple' - simplified instructions (linear, with goto's)
|
||||||
Example:
|
'fallback' - raw instructions without modifications
|
||||||
jadx -d out classes.dex
|
--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
|
### Troubleshooting
|
||||||
##### Out of memory error:
|
Please check wiki page [Troubleshooting Q&A](https://github.com/skylot/jadx/wiki/Troubleshooting-Q&A)
|
||||||
- Reduce processing threads count (`-j` option)
|
|
||||||
- Increase maximum java heap size:
|
|
||||||
* command line (example for linux):
|
|
||||||
`JAVA_OPTS="-Xmx4G" jadx -j 1 some.apk`
|
|
||||||
* edit 'jadx' script (jadx.bat on Windows) and setup bigger heap size:
|
|
||||||
`DEFAULT_JVM_OPTS="-Xmx2500M"`
|
|
||||||
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
|
### Contributing
|
||||||
To support this project you can:
|
To support this project you can:
|
||||||
- Post thoughts about new features/optimizations that important to you
|
- Post thoughts about new features/optimizations that important to you
|
||||||
- Submit bug using one of following patterns:
|
- Submit decompilation issues, please read before proceed: [Open issue](CONTRIBUTING.md#Open-Issue)
|
||||||
* Java code examples which decompiles incorrectly
|
- Open pull request, please follow these rules: [Pull Request Process](CONTRIBUTING.md#Pull-Request-Process)
|
||||||
* Error log and link to _public available_ apk file or app page on Google play
|
|
||||||
|
|
||||||
And any other comments will be very helpfull,
|
|
||||||
because at current stage of development it is very time consuming
|
|
||||||
to **find** new bugs, design and implement new features.
|
|
||||||
Also I need to **prioritize** these task for complete most important at first.
|
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
*Licensed under the Apache 2.0 License*
|
*Licensed under the Apache 2.0 License*
|
||||||
|
|
||||||
*Copyright 2015 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.
|
||||||
-109
@@ -1,109 +0,0 @@
|
|||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id "com.github.kt3k.coveralls" version "2.3.1"
|
|
||||||
id "info.solidsoft.pitest" version "1.1.4"
|
|
||||||
// id "com.github.ben-manes.versions" version "0.8"
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: 'sonar-runner'
|
|
||||||
|
|
||||||
ext.jadxVersion = file('version').readLines().get(0)
|
|
||||||
version = jadxVersion
|
|
||||||
|
|
||||||
subprojects {
|
|
||||||
apply plugin: 'java'
|
|
||||||
apply plugin: 'groovy'
|
|
||||||
apply plugin: 'jacoco'
|
|
||||||
apply plugin: 'com.github.kt3k.coveralls'
|
|
||||||
|
|
||||||
version = jadxVersion
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_6
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_6
|
|
||||||
|
|
||||||
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.10'
|
|
||||||
|
|
||||||
testCompile 'ch.qos.logback:logback-classic:1.1.2'
|
|
||||||
testCompile 'junit:junit:4.12'
|
|
||||||
testCompile 'org.hamcrest:hamcrest-library:1.3'
|
|
||||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
|
||||||
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
|
|
||||||
testCompile 'cglib:cglib-nodep:3.1'
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
mavenLocal()
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
|
|
||||||
jacocoTestReport {
|
|
||||||
reports {
|
|
||||||
xml.enabled = true // coveralls plugin depends on xml format report
|
|
||||||
html.enabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sonar runner configuration */
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
sonarRunner {
|
|
||||||
toolVersion = '2.4'
|
|
||||||
}
|
|
||||||
|
|
||||||
task copyArtifacts(type: Sync, dependsOn: ['jadx-cli:installDist', 'jadx-gui:installDist']) {
|
|
||||||
destinationDir file("$buildDir/jadx")
|
|
||||||
['jadx-cli', 'jadx-gui'].each {
|
|
||||||
from tasks.getByPath(":${it}:installDist").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 pitest(overwrite: true, dependsOn: 'jadx-core:pitest') {
|
|
||||||
}
|
|
||||||
|
|
||||||
task cleanBuildDir(type: Delete) {
|
|
||||||
delete buildDir
|
|
||||||
}
|
|
||||||
|
|
||||||
build.dependsOn(dist, samples)
|
|
||||||
|
|
||||||
clean.dependsOn(cleanBuildDir)
|
|
||||||
|
|
||||||
task wrapper(type: Wrapper) {
|
|
||||||
gradleVersion = '2.3'
|
|
||||||
}
|
|
||||||
@@ -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=https\://services.gradle.org/distributions/gradle-2.3-all.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,20 +0,0 @@
|
|||||||
apply plugin: 'application'
|
|
||||||
|
|
||||||
mainClassName = 'jadx.cli.JadxCLI'
|
|
||||||
applicationName = 'jadx'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile(project(':jadx-core'))
|
|
||||||
compile 'com.beust:jcommander:1.47'
|
|
||||||
compile 'ch.qos.logback:logback-classic:1.1.2'
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationDistribution.with {
|
|
||||||
into('') {
|
|
||||||
from '../.'
|
|
||||||
include 'README.md'
|
|
||||||
include 'NOTICE'
|
|
||||||
include 'LICENSE'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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.JadxDecompiler;
|
|
||||||
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) throws JadxException {
|
public static void main(String[] args) {
|
||||||
|
int result = 0;
|
||||||
try {
|
try {
|
||||||
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
result = execute(args);
|
||||||
if (processArgs(jadxArgs, args)) {
|
} catch (JadxArgsValidateException e) {
|
||||||
processAndSave(jadxArgs);
|
LOG.error("Incorrect arguments: {}", e.getMessage());
|
||||||
}
|
result = 1;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
LOG.error("jadx error: {}", e.getMessage(), e);
|
LOG.error("Process error:", e);
|
||||||
System.exit(1);
|
result = 1;
|
||||||
|
} finally {
|
||||||
|
FileUtils.deleteTempRootDir();
|
||||||
|
System.exit(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException {
|
public static int execute(String[] args) {
|
||||||
JadxDecompiler jadx = new JadxDecompiler(jadxArgs);
|
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||||
jadx.setOutputDir(jadxArgs.getOutDir());
|
if (jadxArgs.processArgs(args)) {
|
||||||
jadx.loadFiles(jadxArgs.getInput());
|
return processAndSave(jadxArgs);
|
||||||
jadx.save();
|
|
||||||
if (jadx.getErrorsCount() != 0) {
|
|
||||||
jadx.printErrorsReport();
|
|
||||||
LOG.error("finished with errors");
|
|
||||||
} else {
|
|
||||||
LOG.info("done");
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean processArgs(JadxCLIArgs jadxArgs, String[] args) throws JadxException {
|
private static int processAndSave(JadxCLIArgs cliArgs) {
|
||||||
if (!jadxArgs.processArgs(args)) {
|
LogHelper.initLogLevel(cliArgs);
|
||||||
return false;
|
LogHelper.setLogLevelsForLoadingStage();
|
||||||
}
|
JadxArgs jadxArgs = cliArgs.toJadxArgs();
|
||||||
if (jadxArgs.getInput().isEmpty()) {
|
jadxArgs.setCodeCache(new NoOpCodeCache());
|
||||||
LOG.error("Please specify input file");
|
jadxArgs.setPluginLoader(new JadxExternalPluginsLoader());
|
||||||
jadxArgs.printUsage();
|
initCodeWriterProvider(jadxArgs);
|
||||||
return false;
|
try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
|
||||||
}
|
jadx.load();
|
||||||
File outputDir = jadxArgs.getOutDir();
|
if (checkForErrors(jadx)) {
|
||||||
if (outputDir == null) {
|
return 1;
|
||||||
String outDirName;
|
}
|
||||||
File file = jadxArgs.getInput().get(0);
|
LogHelper.setLogLevelsForDecompileStage();
|
||||||
String name = file.getName();
|
if (!SingleClassMode.process(jadx, cliArgs)) {
|
||||||
int pos = name.lastIndexOf('.');
|
save(jadx);
|
||||||
if (pos != -1) {
|
}
|
||||||
outDirName = name.substring(0, pos);
|
int errorsCount = jadx.getErrorsCount();
|
||||||
} else {
|
if (errorsCount != 0) {
|
||||||
outDirName = name + "-jadx-out";
|
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");
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,245 +1,644 @@
|
|||||||
package jadx.cli;
|
package jadx.cli;
|
||||||
|
|
||||||
import jadx.api.IJadxArgs;
|
import java.nio.file.Path;
|
||||||
import jadx.api.JadxDecompiler;
|
|
||||||
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.LinkedHashMap;
|
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.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 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, .jar or .class)")
|
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)")
|
@Parameter(names = { "-ds", "--output-dir-src" }, description = "output directory for sources")
|
||||||
protected boolean fallbackMode = false;
|
protected String outDirSrc;
|
||||||
|
|
||||||
@Parameter(names = {"-r", "--no-res"}, description = "do not decode resources")
|
@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;
|
protected boolean skipResources = false;
|
||||||
|
|
||||||
@Parameter(names = {"-s", "--no-src"}, description = "do not decompile source code")
|
@Parameter(names = { "-s", "--no-src" }, description = "do not decompile source code")
|
||||||
protected boolean skipSources = false;
|
protected boolean skipSources = false;
|
||||||
|
|
||||||
@Parameter(names = {"--show-bad-code"}, description = "show inconsistent code (incorrectly decompiled)")
|
@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;
|
protected boolean showInconsistentCode = false;
|
||||||
|
|
||||||
@Parameter(names = {"--cfg"}, description = "save methods control flow graph to dot file")
|
@Parameter(names = { "--no-xml-pretty-print" }, description = "do not prettify XML")
|
||||||
protected boolean cfgOutput = false;
|
protected boolean skipXmlPrettyPrint = false;
|
||||||
|
|
||||||
@Parameter(names = {"--raw-cfg"}, description = "save methods control flow graph (use raw instructions)")
|
@Parameter(names = { "--no-imports" }, description = "disable use of imports, always write entire package name")
|
||||||
protected boolean rawCfgOutput = false;
|
protected boolean useImports = true;
|
||||||
|
|
||||||
@Parameter(names = {"-v", "--verbose"}, description = "verbose output")
|
@Parameter(names = { "--no-debug-info" }, description = "disable debug info parsing and processing")
|
||||||
protected boolean verbose = false;
|
protected boolean debugInfo = true;
|
||||||
|
|
||||||
@Parameter(names = {"--deobf"}, description = "activate deobfuscation")
|
@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;
|
protected boolean deobfuscationOn = false;
|
||||||
|
|
||||||
@Parameter(names = {"--deobf-min"}, description = "min length of name")
|
@Parameter(names = { "--deobf-min" }, description = "min length of name, renamed if shorter")
|
||||||
protected int deobfuscationMinLength = 2;
|
protected int deobfuscationMinLength = 3;
|
||||||
|
|
||||||
@Parameter(names = {"--deobf-max"}, description = "max length of name")
|
@Parameter(names = { "--deobf-max" }, description = "max length of name, renamed if longer")
|
||||||
protected int deobfuscationMaxLength = 64;
|
protected int deobfuscationMaxLength = 64;
|
||||||
|
|
||||||
@Parameter(names = {"--deobf-rewrite-cfg"}, description = "force to save deobfuscation map")
|
@Parameter(
|
||||||
protected boolean deobfuscationForceSave = false;
|
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 = {"-h", "--help"}, description = "print this help", help = true)
|
@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;
|
||||||
|
|
||||||
|
@Parameter(names = { "--raw-cfg" }, description = "save methods control flow graph (use raw instructions)")
|
||||||
|
protected boolean rawCfgOutput = false;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
|
||||||
|
@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 boolean processArgs(String[] args) {
|
public boolean processArgs(String[] args) {
|
||||||
return parse(args) && process();
|
JCommanderWrapper<JadxCLIArgs> jcw = new JCommanderWrapper<>(this);
|
||||||
|
return jcw.parse(args) && process(jcw);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean 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.
|
||||||
return true;
|
*/
|
||||||
} catch (ParameterException e) {
|
public boolean overrideProvided(String[] args) {
|
||||||
System.err.println("Arguments parse error: " + e.getMessage());
|
JCommanderWrapper<JadxCLIArgs> jcw = new JCommanderWrapper<>(newInstance());
|
||||||
printUsage();
|
if (!jcw.parse(args)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
jcw.overrideProvided(this);
|
||||||
|
return process(jcw);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean process() {
|
protected JadxCLIArgs newInstance() {
|
||||||
if (isPrintHelp()) {
|
return new JadxCLIArgs();
|
||||||
printUsage();
|
}
|
||||||
|
|
||||||
|
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;
|
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();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
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: " + JadxDecompiler.getVersion());
|
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) {
|
||||||
Map<String, ParameterDescription> paramsMap = new LinkedHashMap<String, ParameterDescription>(params.size());
|
args.setDecompilationMode(DecompilationMode.FALLBACK);
|
||||||
int maxNamesLen = 0;
|
} else {
|
||||||
for (ParameterDescription p : params) {
|
args.setDecompilationMode(decompilationMode);
|
||||||
paramsMap.put(p.getParameterized().getName(), p);
|
|
||||||
int len = p.getNames().length();
|
|
||||||
if (len > maxNamesLen) {
|
|
||||||
maxNamesLen = len;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Field[] fields = JadxCLIArgs.class.getDeclaredFields();
|
args.setShowInconsistentCode(showInconsistentCode);
|
||||||
for (Field f : fields) {
|
args.setCfgOutput(cfgOutput);
|
||||||
String name = f.getName();
|
args.setRawCFGOutput(rawCfgOutput);
|
||||||
ParameterDescription p = paramsMap.get(name);
|
args.setReplaceConsts(replaceConsts);
|
||||||
if (p == null) {
|
if (userRenamesMappingsPath != null) {
|
||||||
continue;
|
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);
|
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String getOutDirSrc() {
|
||||||
public File getOutDir() {
|
return outDirSrc;
|
||||||
return outputDir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSkipResources() {
|
public boolean isSkipResources() {
|
||||||
return skipResources;
|
return skipResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSkipSources() {
|
public boolean isSkipSources() {
|
||||||
return skipSources;
|
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() {
|
||||||
|
return useDx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecompilationMode getDecompilationMode() {
|
||||||
|
return decompilationMode;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isShowInconsistentCode() {
|
public boolean isShowInconsistentCode() {
|
||||||
return showInconsistentCode;
|
return showInconsistentCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isUseImports() {
|
||||||
public boolean isVerbose() {
|
return useImports;
|
||||||
return verbose;
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDeobfuscationOn() {
|
public boolean isDeobfuscationOn() {
|
||||||
return deobfuscationOn;
|
return deobfuscationOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDeobfuscationMinLength() {
|
public int getDeobfuscationMinLength() {
|
||||||
return deobfuscationMinLength;
|
return deobfuscationMinLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDeobfuscationMaxLength() {
|
public int getDeobfuscationMaxLength() {
|
||||||
return deobfuscationMaxLength;
|
return deobfuscationMaxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String getDeobfuscationWhitelistStr() {
|
||||||
public boolean isDeobfuscationForceSave() {
|
return deobfuscationWhitelistStr;
|
||||||
return deobfuscationForceSave;
|
}
|
||||||
|
|
||||||
|
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,27 +0,0 @@
|
|||||||
ext.jadxClasspath = 'clsp-data/android-5.1.jar'
|
|
||||||
|
|
||||||
apply plugin: "info.solidsoft.pitest"
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
runtime files(jadxClasspath)
|
|
||||||
|
|
||||||
compile files('lib/dx-1.10.jar')
|
|
||||||
compile 'commons-io:commons-io:2.4'
|
|
||||||
compile 'org.ow2.asm:asm:5.0.3'
|
|
||||||
compile 'com.intellij:annotations:12.0'
|
|
||||||
|
|
||||||
testCompile 'org.smali:smali:2.0.3'
|
|
||||||
}
|
|
||||||
|
|
||||||
task packTests(type: Jar) {
|
|
||||||
classifier = 'tests'
|
|
||||||
from sourceSets.test.output
|
|
||||||
}
|
|
||||||
|
|
||||||
pitest {
|
|
||||||
excludedMethods = ['toString']
|
|
||||||
threads = 4
|
|
||||||
enableDefaultIncrementalAnalysis = true
|
|
||||||
outputFormats = ['XML', 'HTML']
|
|
||||||
jvmArgs = ['-Xmx12G']
|
|
||||||
}
|
|
||||||
@@ -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.
Binary file not shown.
@@ -1,58 +0,0 @@
|
|||||||
package jadx.api;
|
|
||||||
|
|
||||||
public final class CodePosition {
|
|
||||||
|
|
||||||
private final JavaClass cls;
|
|
||||||
private final int line;
|
|
||||||
private final int offset;
|
|
||||||
|
|
||||||
public CodePosition(JavaClass cls, int line, int offset) {
|
|
||||||
this.cls = cls;
|
|
||||||
this.line = line;
|
|
||||||
this.offset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CodePosition(int line, int offset) {
|
|
||||||
this.cls = null;
|
|
||||||
this.line = line;
|
|
||||||
this.offset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JavaClass getJavaClass() {
|
|
||||||
return cls;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLine() {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOffset() {
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSet() {
|
|
||||||
return line != 0 || offset != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
CodePosition that = (CodePosition) o;
|
|
||||||
return line == that.line && offset == that.offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return line + 31 * offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return line + ":" + offset + (cls != null ? " " + cls : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,71 +0,0 @@
|
|||||||
package jadx.api;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class DefaultJadxArgs implements IJadxArgs {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getOutDir() {
|
|
||||||
return new File("jadx-output");
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 isShowInconsistentCode() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isVerbose() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSkipResources() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSkipSources() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDeobfuscationOn() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDeobfuscationMinLength() {
|
|
||||||
return Integer.MIN_VALUE + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDeobfuscationMaxLength() {
|
|
||||||
return Integer.MAX_VALUE - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDeobfuscationForceSave() {
|
|
||||||
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,31 +0,0 @@
|
|||||||
package jadx.api;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public interface IJadxArgs {
|
|
||||||
File getOutDir();
|
|
||||||
|
|
||||||
int getThreadsCount();
|
|
||||||
|
|
||||||
boolean isCFGOutput();
|
|
||||||
|
|
||||||
boolean isRawCFGOutput();
|
|
||||||
|
|
||||||
boolean isFallbackMode();
|
|
||||||
|
|
||||||
boolean isShowInconsistentCode();
|
|
||||||
|
|
||||||
boolean isVerbose();
|
|
||||||
|
|
||||||
boolean isSkipResources();
|
|
||||||
|
|
||||||
boolean isSkipSources();
|
|
||||||
|
|
||||||
boolean isDeobfuscationOn();
|
|
||||||
|
|
||||||
int getDeobfuscationMinLength();
|
|
||||||
|
|
||||||
int getDeobfuscationMaxLength();
|
|
||||||
|
|
||||||
boolean isDeobfuscationForceSave();
|
|
||||||
}
|
|
||||||
@@ -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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,127 +1,266 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
import jadx.core.Jadx;
|
import java.io.Closeable;
|
||||||
import jadx.core.ProcessClass;
|
|
||||||
import jadx.core.codegen.CodeGen;
|
|
||||||
import jadx.core.codegen.CodeWriter;
|
|
||||||
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.exceptions.DecodeException;
|
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
|
||||||
import jadx.core.utils.files.InputFile;
|
|
||||||
import jadx.core.xmlgen.BinaryXMLParser;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.function.Predicate;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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:
|
* Jadx API usage example:
|
||||||
* <pre><code>
|
*
|
||||||
* JadxDecompiler jadx = new JadxDecompiler();
|
* <pre>
|
||||||
* jadx.loadFile(new File("classes.dex"));
|
* <code>
|
||||||
* jadx.setOutputDir(new File("out"));
|
*
|
||||||
* jadx.save();
|
* JadxArgs args = new JadxArgs();
|
||||||
* </code></pre>
|
* args.getInputFiles().add(new File("test.apk"));
|
||||||
* <p/>
|
* args.setOutDir(new File("jadx-test-output"));
|
||||||
* Instead of 'save()' you can get list of decompiled classes:
|
* try (JadxDecompiler jadx = new JadxDecompiler(args)) {
|
||||||
* <pre><code>
|
* jadx.load();
|
||||||
|
* jadx.save();
|
||||||
|
* }
|
||||||
|
* </code>
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* Instead of 'save()' you can iterate over decompiled classes:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <code>
|
||||||
|
*
|
||||||
* for(JavaClass cls : jadx.getClasses()) {
|
* for(JavaClass cls : jadx.getClasses()) {
|
||||||
* System.out.println(cls.getCode());
|
* System.out.println(cls.getCode());
|
||||||
* }
|
* }
|
||||||
* </code></pre>
|
* </code>
|
||||||
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public final class JadxDecompiler {
|
public final class JadxDecompiler implements Closeable {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
|
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
|
||||||
|
|
||||||
private final IJadxArgs args;
|
private final JadxArgs args;
|
||||||
private final List<InputFile> inputFiles = new ArrayList<InputFile>();
|
private final JadxPluginManager pluginManager = new JadxPluginManager(this);
|
||||||
|
private final List<ICodeLoader> loadedInputs = new ArrayList<>();
|
||||||
private File outDir;
|
|
||||||
|
|
||||||
private RootNode root;
|
private RootNode root;
|
||||||
private List<IDexTreeVisitor> passes;
|
|
||||||
private CodeGen codeGen;
|
|
||||||
|
|
||||||
private List<JavaClass> classes;
|
private List<JavaClass> classes;
|
||||||
private List<ResourceFile> resources;
|
private List<ResourceFile> resources;
|
||||||
|
|
||||||
private BinaryXMLParser xmlParser;
|
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() {
|
public JadxDecompiler() {
|
||||||
this(new DefaultJadxArgs());
|
this(new JadxArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
public JadxDecompiler(IJadxArgs jadxArgs) {
|
public JadxDecompiler(JadxArgs args) {
|
||||||
this.args = jadxArgs;
|
this.args = args;
|
||||||
this.outDir = jadxArgs.getOutDir();
|
}
|
||||||
|
|
||||||
|
public void load() {
|
||||||
reset();
|
reset();
|
||||||
init();
|
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 setOutputDir(File outDir) {
|
public void reloadPasses() {
|
||||||
this.outDir = outDir;
|
LOG.info("reloading (passes only) ...");
|
||||||
init();
|
customPasses.clear();
|
||||||
|
root.resetPasses();
|
||||||
|
events.reset();
|
||||||
|
loadPlugins();
|
||||||
|
root.mergePasses(customPasses);
|
||||||
|
root.restartVisitors();
|
||||||
|
root.initPasses();
|
||||||
|
loadFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
private void loadInputFiles() {
|
||||||
if (outDir == null) {
|
loadedInputs.clear();
|
||||||
outDir = new DefaultJadxArgs().getOutDir();
|
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);
|
||||||
}
|
}
|
||||||
this.passes = Jadx.getPassesList(args, outDir);
|
|
||||||
this.codeGen = new CodeGen(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
private void reset() {
|
||||||
|
root = null;
|
||||||
classes = null;
|
classes = null;
|
||||||
resources = null;
|
resources = null;
|
||||||
xmlParser = null;
|
binaryXmlParser = null;
|
||||||
root = null;
|
protoXmlParser = null;
|
||||||
passes = null;
|
events.reset();
|
||||||
codeGen = null;
|
}
|
||||||
|
|
||||||
|
@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() {
|
public static String getVersion() {
|
||||||
return Jadx.getVersion();
|
return Jadx.getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadFile(File file) throws JadxException {
|
|
||||||
loadFiles(Collections.singletonList(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadFiles(List<File> files) throws JadxException {
|
|
||||||
if (files.isEmpty()) {
|
|
||||||
throw new JadxException("Empty file list");
|
|
||||||
}
|
|
||||||
inputFiles.clear();
|
|
||||||
for (File file : files) {
|
|
||||||
try {
|
|
||||||
inputFiles.add(new InputFile(file));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new JadxException("Error load file: " + file, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() {
|
public void save() {
|
||||||
save(!args.isSkipSources(), !args.isSkipResources());
|
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() {
|
public void saveSources() {
|
||||||
save(true, false);
|
save(true, false);
|
||||||
}
|
}
|
||||||
@@ -131,111 +270,173 @@ public final class JadxDecompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void save(boolean saveSources, boolean saveResources) {
|
private void save(boolean saveSources, boolean saveResources) {
|
||||||
try {
|
ITaskExecutor executor = getSaveTasks(saveSources, saveResources);
|
||||||
ExecutorService ex = getSaveExecutor(saveSources, saveResources);
|
executor.execute();
|
||||||
ex.shutdown();
|
executor.awaitTermination();
|
||||||
ex.awaitTermination(1, TimeUnit.DAYS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new JadxRuntimeException("Save interrupted", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ITaskExecutor getSaveTaskExecutor() {
|
||||||
|
return getSaveTasks(!args.isSkipSources(), !args.isSkipResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
public ExecutorService getSaveExecutor() {
|
public ExecutorService getSaveExecutor() {
|
||||||
return getSaveExecutor(!args.isSkipSources(), !args.isSkipResources());
|
ITaskExecutor executor = getSaveTaskExecutor();
|
||||||
|
executor.execute();
|
||||||
|
return executor.getInternalExecutor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExecutorService getSaveExecutor(boolean saveSources, boolean saveResources) {
|
@Deprecated(forRemoval = true)
|
||||||
|
public List<Runnable> getSaveTasks() {
|
||||||
|
return Collections.singletonList(this::save);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskExecutor getSaveTasks(boolean saveSources, boolean saveResources) {
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
throw new JadxRuntimeException("No loaded files");
|
throw new JadxRuntimeException("No loaded files");
|
||||||
}
|
}
|
||||||
int threadsCount = args.getThreadsCount();
|
File sourcesOutDir;
|
||||||
LOG.debug("processing threads count: {}", threadsCount);
|
File resOutDir;
|
||||||
|
ExportGradleTask gradleExportTask;
|
||||||
LOG.info("processing ...");
|
if (args.isExportAsGradleProject()) {
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(threadsCount);
|
gradleExportTask = new ExportGradleTask(resources, root, args.getOutDir());
|
||||||
if (saveSources) {
|
gradleExportTask.init();
|
||||||
for (final JavaClass cls : getClasses()) {
|
sourcesOutDir = gradleExportTask.getSrcOutDir();
|
||||||
executor.execute(new Runnable() {
|
resOutDir = gradleExportTask.getResOutDir();
|
||||||
@Override
|
} else {
|
||||||
public void run() {
|
sourcesOutDir = args.getOutDirSrc();
|
||||||
cls.decompile();
|
resOutDir = args.getOutDirRes();
|
||||||
SaveCode.save(outDir, args, cls.getClassNode());
|
gradleExportTask = null;
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskExecutor executor = new TaskExecutor();
|
||||||
|
executor.setThreadsCount(args.getThreadsCount());
|
||||||
if (saveResources) {
|
if (saveResources) {
|
||||||
for (final ResourceFile resourceFile : getResources()) {
|
// save resources first because decompilation can stop or fail
|
||||||
executor.execute(new Runnable() {
|
appendResourcesSaveTasks(executor, resOutDir);
|
||||||
@Override
|
}
|
||||||
public void run() {
|
if (saveSources) {
|
||||||
if (ResourceType.isSupportedForUnpack(resourceFile.getType())) {
|
appendSourcesSave(executor, sourcesOutDir);
|
||||||
CodeWriter cw = resourceFile.getContent();
|
}
|
||||||
if (cw != null) {
|
if (gradleExportTask != null) {
|
||||||
cw.save(new File(outDir, resourceFile.getName()));
|
executor.addSequentialTask(gradleExportTask);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return executor;
|
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() {
|
public List<JavaClass> getClasses() {
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
if (classes == null) {
|
if (classes == null) {
|
||||||
List<ClassNode> classNodeList = root.getClasses(false);
|
List<ClassNode> classNodeList = root.getClasses();
|
||||||
List<JavaClass> clsList = new ArrayList<JavaClass>(classNodeList.size());
|
List<JavaClass> clsList = new ArrayList<>(classNodeList.size());
|
||||||
for (ClassNode classNode : classNodeList) {
|
for (ClassNode classNode : classNodeList) {
|
||||||
clsList.add(new JavaClass(classNode, this));
|
if (classNode.contains(AFlag.DONT_GENERATE)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!classNode.getClassInfo().isInner()) {
|
||||||
|
clsList.add(convertClassNode(classNode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
classes = Collections.unmodifiableList(clsList);
|
classes = Collections.unmodifiableList(clsList);
|
||||||
}
|
}
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ResourceFile> getResources() {
|
public List<JavaClass> getClassesWithInners() {
|
||||||
|
return Utils.collectionMap(root.getClasses(), this::convertClassNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List<ResourceFile> getResources() {
|
||||||
if (resources == null) {
|
if (resources == null) {
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
resources = new ResourcesLoader(this).load(inputFiles);
|
resources = new ResourcesLoader(this).load();
|
||||||
}
|
}
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<JavaPackage> getPackages() {
|
public List<JavaPackage> getPackages() {
|
||||||
List<JavaClass> classList = getClasses();
|
return Utils.collectionMap(root.getPackages(), this::convertPackageNode);
|
||||||
if (classList.isEmpty()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
Map<String, List<JavaClass>> map = new HashMap<String, List<JavaClass>>();
|
|
||||||
for (JavaClass javaClass : classList) {
|
|
||||||
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.getName().compareTo(o2.getName());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(packages);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getErrorsCount() {
|
public int getErrorsCount() {
|
||||||
@@ -245,69 +446,264 @@ public final class JadxDecompiler {
|
|||||||
return root.getErrorsCounter().getErrorCount();
|
return root.getErrorsCounter().getErrorCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getWarnsCount() {
|
||||||
|
if (root == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return root.getErrorsCounter().getWarnsCount();
|
||||||
|
}
|
||||||
|
|
||||||
public void printErrorsReport() {
|
public void printErrorsReport() {
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
root.getClsp().printMissingClasses();
|
||||||
root.getErrorsCounter().printReport();
|
root.getErrorsCounter().printReport();
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse() throws DecodeException {
|
/**
|
||||||
reset();
|
* Internal API. Not Stable!
|
||||||
init();
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
root = new RootNode(args);
|
public RootNode getRoot() {
|
||||||
LOG.info("loading ...");
|
|
||||||
root.load(inputFiles);
|
|
||||||
|
|
||||||
root.initClassPath();
|
|
||||||
root.loadResources(getResources());
|
|
||||||
root.initAppResClass();
|
|
||||||
|
|
||||||
initVisitors();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initVisitors() {
|
|
||||||
for (IDexTreeVisitor pass : passes) {
|
|
||||||
try {
|
|
||||||
pass.init(root);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.error("Visitor init failed: {}", pass.getClass().getSimpleName(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void processClass(ClassNode cls) {
|
|
||||||
ProcessClass.process(cls, passes, codeGen);
|
|
||||||
}
|
|
||||||
|
|
||||||
RootNode getRoot() {
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
BinaryXMLParser getXmlParser() {
|
synchronized BinaryXMLParser getBinaryXmlParser() {
|
||||||
if (xmlParser == null) {
|
if (binaryXmlParser == null) {
|
||||||
xmlParser = new BinaryXMLParser(root);
|
binaryXmlParser = new BinaryXMLParser(root);
|
||||||
}
|
}
|
||||||
return xmlParser;
|
return binaryXmlParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
JavaClass findJavaClass(ClassNode cls) {
|
synchronized ProtoXMLParser getProtoXmlParser() {
|
||||||
if (cls == null) {
|
if (protoXmlParser == null) {
|
||||||
return null;
|
protoXmlParser = new ProtoXMLParser(root);
|
||||||
}
|
}
|
||||||
for (JavaClass javaClass : getClasses()) {
|
return protoXmlParser;
|
||||||
if (javaClass.getClassNode().equals(cls)) {
|
}
|
||||||
return javaClass;
|
|
||||||
|
/**
|
||||||
|
* 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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IJadxArgs getArgs() {
|
@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;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "jadx decompiler " + getVersion();
|
return "jadx decompiler " + getVersion();
|
||||||
|
|||||||
@@ -1,28 +1,42 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
import jadx.core.codegen.CodeWriter;
|
|
||||||
import jadx.core.dex.attributes.AFlag;
|
|
||||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
|
||||||
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;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.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 {
|
public final class JavaClass implements JavaNode {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JavaClass.class);
|
||||||
|
|
||||||
private final JadxDecompiler decompiler;
|
private final JadxDecompiler decompiler;
|
||||||
private final ClassNode cls;
|
private final ClassNode cls;
|
||||||
private final JavaClass parent;
|
private final JavaClass parent;
|
||||||
|
|
||||||
private List<JavaClass> innerClasses = Collections.emptyList();
|
private List<JavaClass> innerClasses = Collections.emptyList();
|
||||||
|
private List<JavaClass> inlinedClasses = Collections.emptyList();
|
||||||
private List<JavaField> fields = Collections.emptyList();
|
private List<JavaField> fields = Collections.emptyList();
|
||||||
private List<JavaMethod> methods = Collections.emptyList();
|
private List<JavaMethod> methods = Collections.emptyList();
|
||||||
|
private boolean listsLoaded;
|
||||||
|
|
||||||
JavaClass(ClassNode classNode, JadxDecompiler decompiler) {
|
JavaClass(ClassNode classNode, JadxDecompiler decompiler) {
|
||||||
this.decompiler = decompiler;
|
this.decompiler = decompiler;
|
||||||
@@ -40,51 +54,114 @@ public final class JavaClass implements JavaNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getCode() {
|
public String getCode() {
|
||||||
CodeWriter code = cls.getCode();
|
return getCodeInfo().getCodeStr();
|
||||||
if (code == null) {
|
}
|
||||||
decompile();
|
|
||||||
code = cls.getCode();
|
public @NotNull ICodeInfo getCodeInfo() {
|
||||||
|
ICodeInfo code = load();
|
||||||
|
if (code != null) {
|
||||||
|
return code;
|
||||||
}
|
}
|
||||||
if (code == null) {
|
return cls.decompile();
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return code.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decompile() {
|
public void decompile() {
|
||||||
if (decompiler == null) {
|
load();
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cls.getCode() == null) {
|
|
||||||
decompiler.processClass(cls);
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassNode getClassNode() {
|
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;
|
return cls;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void load() {
|
/**
|
||||||
|
* 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 {
|
||||||
|
code = cls.decompile();
|
||||||
|
}
|
||||||
|
|
||||||
|
JadxDecompiler rootDecompiler = getRootDecompiler();
|
||||||
int inClsCount = cls.getInnerClasses().size();
|
int inClsCount = cls.getInnerClasses().size();
|
||||||
if (inClsCount != 0) {
|
if (inClsCount != 0) {
|
||||||
List<JavaClass> list = new ArrayList<JavaClass>(inClsCount);
|
List<JavaClass> list = new ArrayList<>(inClsCount);
|
||||||
for (ClassNode inner : cls.getInnerClasses()) {
|
for (ClassNode inner : cls.getInnerClasses()) {
|
||||||
if (!inner.contains(AFlag.DONT_GENERATE)) {
|
if (!inner.contains(AFlag.DONT_GENERATE)) {
|
||||||
JavaClass javaClass = new JavaClass(inner, this);
|
JavaClass javaClass = rootDecompiler.convertClassNode(inner);
|
||||||
javaClass.load();
|
javaClass.load();
|
||||||
list.add(javaClass);
|
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) {
|
||||||
List<JavaField> flds = new ArrayList<JavaField>(fieldsCount);
|
List<JavaField> flds = new ArrayList<>(fieldsCount);
|
||||||
for (FieldNode f : cls.getFields()) {
|
for (FieldNode f : cls.getFields()) {
|
||||||
if (!f.contains(AFlag.DONT_GENERATE)) {
|
if (!f.contains(AFlag.DONT_GENERATE)) {
|
||||||
flds.add(new JavaField(f, this));
|
flds.add(rootDecompiler.convertFieldNode(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.fields = Collections.unmodifiableList(flds);
|
this.fields = Collections.unmodifiableList(flds);
|
||||||
@@ -92,63 +169,69 @@ public final class JavaClass implements JavaNode {
|
|||||||
|
|
||||||
int methodsCount = cls.getMethods().size();
|
int methodsCount = cls.getMethods().size();
|
||||||
if (methodsCount != 0) {
|
if (methodsCount != 0) {
|
||||||
List<JavaMethod> mths = new ArrayList<JavaMethod>(methodsCount);
|
List<JavaMethod> mths = new ArrayList<>(methodsCount);
|
||||||
for (MethodNode m : cls.getMethods()) {
|
for (MethodNode m : cls.getMethods()) {
|
||||||
if (!m.contains(AFlag.DONT_GENERATE)) {
|
if (!m.contains(AFlag.DONT_GENERATE)) {
|
||||||
mths.add(new JavaMethod(this, 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<CodePosition, Object> getCodeAnnotations() {
|
JadxDecompiler getRootDecompiler() {
|
||||||
decompile();
|
if (parent != null) {
|
||||||
return cls.getCode().getAnnotations();
|
return parent.getRootDecompiler();
|
||||||
|
}
|
||||||
|
return decompiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodePosition getDefinitionPosition(int line, int offset) {
|
public ICodeAnnotation getAnnotationAt(int pos) {
|
||||||
Map<CodePosition, Object> map = getCodeAnnotations();
|
return getCodeInfo().getCodeMetadata().getAt(pos);
|
||||||
if (map.isEmpty()) {
|
}
|
||||||
|
|
||||||
|
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 null;
|
||||||
}
|
});
|
||||||
Object obj = map.get(new CodePosition(line, offset));
|
return result;
|
||||||
if (!(obj instanceof LineAttrNode)) {
|
}
|
||||||
return null;
|
|
||||||
}
|
@Override
|
||||||
ClassNode clsNode = null;
|
public List<JavaNode> getUseIn() {
|
||||||
if (obj instanceof ClassNode) {
|
return getRootDecompiler().convertNodes(cls.getUseIn());
|
||||||
clsNode = (ClassNode) obj;
|
|
||||||
} else if (obj instanceof MethodNode) {
|
|
||||||
clsNode = ((MethodNode) obj).getParentClass();
|
|
||||||
} else if (obj instanceof FieldNode) {
|
|
||||||
clsNode = ((FieldNode) obj).getParentClass();
|
|
||||||
}
|
|
||||||
if (clsNode == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
clsNode = clsNode.getTopParentClass();
|
|
||||||
JavaClass jCls = decompiler.findJavaClass(clsNode);
|
|
||||||
if (jCls == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
jCls.decompile();
|
|
||||||
int defLine = ((LineAttrNode) obj).getDecompiledLine();
|
|
||||||
if (defLine == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new CodePosition(jCls, defLine, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getSourceLine(int decompiledLine) {
|
public Integer getSourceLine(int decompiledLine) {
|
||||||
decompile();
|
return getCodeInfo().getCodeMetadata().getLineMapping().get(decompiledLine);
|
||||||
return cls.getCode().getLineMapping().get(decompiledLine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,6 +244,10 @@ public final class JavaClass implements JavaNode {
|
|||||||
return cls.getFullName();
|
return cls.getFullName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRawName() {
|
||||||
|
return cls.getRawName();
|
||||||
|
}
|
||||||
|
|
||||||
public String getPackage() {
|
public String getPackage() {
|
||||||
return cls.getPackage();
|
return cls.getPackage();
|
||||||
}
|
}
|
||||||
@@ -170,27 +257,89 @@ public final class JavaClass implements JavaNode {
|
|||||||
return parent;
|
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() {
|
||||||
decompile();
|
load();
|
||||||
return innerClasses;
|
return innerClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<JavaClass> getInlinedClasses() {
|
||||||
|
load();
|
||||||
|
return inlinedClasses;
|
||||||
|
}
|
||||||
|
|
||||||
public List<JavaField> getFields() {
|
public List<JavaField> getFields() {
|
||||||
decompile();
|
load();
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<JavaMethod> getMethods() {
|
public List<JavaMethod> getMethods() {
|
||||||
decompile();
|
load();
|
||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDecompiledLine() {
|
@Nullable
|
||||||
return cls.getDecompiledLine();
|
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
|
@Override
|
||||||
@@ -205,6 +354,6 @@ public final class JavaClass implements JavaNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return cls.getFullName() + "[ " + getFullName() + " ]";
|
return getFullName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
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;
|
||||||
@@ -9,7 +15,7 @@ public final class JavaField implements JavaNode {
|
|||||||
private final FieldNode field;
|
private final FieldNode field;
|
||||||
private final JavaClass parent;
|
private final JavaClass parent;
|
||||||
|
|
||||||
JavaField(FieldNode f, JavaClass cls) {
|
JavaField(JavaClass cls, FieldNode f) {
|
||||||
this.field = f;
|
this.field = f;
|
||||||
this.parent = cls;
|
this.parent = cls;
|
||||||
}
|
}
|
||||||
@@ -21,7 +27,11 @@ public final class JavaField implements JavaNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
return parent.getFullName() + "." + getName();
|
return parent.getFullName() + '.' + getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRawName() {
|
||||||
|
return field.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -29,15 +39,67 @@ public final class JavaField implements JavaNode {
|
|||||||
return parent;
|
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,12 +1,26 @@
|
|||||||
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.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 {
|
public final class JavaMethod implements JavaNode {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JavaMethod.class);
|
||||||
|
|
||||||
private final MethodNode mth;
|
private final MethodNode mth;
|
||||||
private final JavaClass parent;
|
private final JavaClass parent;
|
||||||
|
|
||||||
@@ -30,16 +44,51 @@ public final class JavaMethod implements JavaNode {
|
|||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaClass getTopParentClass() {
|
||||||
|
return parent.getTopParentClass();
|
||||||
|
}
|
||||||
|
|
||||||
public AccessInfo getAccessFlags() {
|
public AccessInfo getAccessFlags() {
|
||||||
return mth.getAccessFlags();
|
return mth.getAccessFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
||||||
@@ -50,7 +99,49 @@ public final class JavaMethod implements JavaNode {
|
|||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
|
|
||||||
public interface JavaNode {
|
public interface JavaNode {
|
||||||
|
|
||||||
|
ICodeNodeRef getCodeNodeRef();
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
String getFullName();
|
String getFullName();
|
||||||
|
|
||||||
JavaClass getDeclaringClass();
|
JavaClass getDeclaringClass();
|
||||||
|
|
||||||
|
JavaClass getTopParentClass();
|
||||||
|
|
||||||
|
int getDefPos();
|
||||||
|
|
||||||
|
List<JavaNode> getUseIn();
|
||||||
|
|
||||||
|
void removeAlias();
|
||||||
|
|
||||||
|
boolean isOwnCodeAnnotation(ICodeAnnotation ann);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,128 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus.Internal;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
private final String name;
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
private final List<JavaClass> classes;
|
import jadx.core.dex.info.PackageInfo;
|
||||||
|
import jadx.core.dex.nodes.PackageNode;
|
||||||
|
|
||||||
JavaPackage(String name, List<JavaClass> classes) {
|
public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
||||||
this.name = name;
|
private final PackageNode pkgNode;
|
||||||
|
private final List<JavaClass> classes;
|
||||||
|
private final List<JavaPackage> subPkgs;
|
||||||
|
|
||||||
|
JavaPackage(PackageNode pkgNode, List<JavaClass> classes, List<JavaPackage> subPkgs) {
|
||||||
|
this.pkgNode = pkgNode;
|
||||||
this.classes = classes;
|
this.classes = classes;
|
||||||
|
this.subPkgs = subPkgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return pkgNode.getAliasPkgInfo().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
// TODO: store full package name
|
return pkgNode.getAliasPkgInfo().getFullName();
|
||||||
return name;
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
public void removeAlias() {
|
||||||
|
pkgNode.removeAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParentRenamed() {
|
||||||
|
PackageInfo parent = pkgNode.getPkgInfo().getParentPkg();
|
||||||
|
PackageInfo aliasParent = pkgNode.getAliasPkgInfo().getParentPkg();
|
||||||
|
return !Objects.equals(parent, aliasParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICodeNodeRef getCodeNodeRef() {
|
||||||
|
return pkgNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public PackageNode getPkgNode() {
|
||||||
|
return pkgNode;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JavaClass getDeclaringClass() {
|
public JavaClass getDeclaringClass() {
|
||||||
return null;
|
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
|
@Override
|
||||||
public int compareTo(@NotNull JavaPackage o) {
|
public int compareTo(@NotNull JavaPackage o) {
|
||||||
return name.compareTo(o.name);
|
return pkgNode.compareTo(o.pkgNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -47,16 +134,16 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
JavaPackage that = (JavaPackage) o;
|
JavaPackage that = (JavaPackage) o;
|
||||||
return name.equals(that.name);
|
return pkgNode.equals(that.pkgNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
import jadx.core.codegen.CodeWriter;
|
|
||||||
|
|
||||||
import java.io.File;
|
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 class ResourceFile {
|
||||||
|
|
||||||
public static final class ZipRef {
|
public static final class ZipRef {
|
||||||
@@ -33,22 +35,38 @@ public class ResourceFile {
|
|||||||
private final String name;
|
private final String name;
|
||||||
private final ResourceType type;
|
private final ResourceType type;
|
||||||
private ZipRef zipRef;
|
private ZipRef zipRef;
|
||||||
|
private String deobfName;
|
||||||
|
|
||||||
ResourceFile(JadxDecompiler decompiler, String name, ResourceType type) {
|
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.decompiler = decompiler;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getOriginalName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDeobfName() {
|
||||||
|
return deobfName != null ? deobfName : name;
|
||||||
|
}
|
||||||
|
|
||||||
public ResourceType getType() {
|
public ResourceType getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodeWriter getContent() {
|
public ResContainer loadContent() {
|
||||||
return ResourcesLoader.loadContent(decompiler, this);
|
return ResourcesLoader.loadContent(decompiler, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +74,28 @@ public class ResourceFile {
|
|||||||
this.zipRef = zipRef;
|
this.zipRef = zipRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
ZipRef getZipRef() {
|
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;
|
return zipRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ResourceFile{name='" + name + '\'' + ", type=" + type + "}";
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,20 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
public enum ResourceType {
|
public enum ResourceType {
|
||||||
CODE(".dex", ".class"),
|
CODE(".dex", ".jar", ".class"),
|
||||||
MANIFEST("AndroidManifest.xml"),
|
XML(".xml"),
|
||||||
XML(".xml"), // TODO binary or not?
|
ARSC(".arsc"),
|
||||||
ARSC(".arsc"), // TODO decompile !!!
|
FONT(".ttf", ".otf"),
|
||||||
FONT(".ttf"),
|
|
||||||
IMG(".png", ".gif", ".jpg"),
|
IMG(".png", ".gif", ".jpg"),
|
||||||
|
MEDIA(".mp3", ".wav"),
|
||||||
LIB(".so"),
|
LIB(".so"),
|
||||||
|
MANIFEST,
|
||||||
UNKNOWN;
|
UNKNOWN;
|
||||||
|
|
||||||
private final String[] exts;
|
private final String[] exts;
|
||||||
@@ -20,31 +27,34 @@ public enum ResourceType {
|
|||||||
return exts;
|
return exts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResourceType getFileType(String fileName) {
|
private static final Map<String, ResourceType> EXT_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
for (ResourceType type : ResourceType.values()) {
|
for (ResourceType type : ResourceType.values()) {
|
||||||
for (String ext : type.getExts()) {
|
for (String ext : type.getExts()) {
|
||||||
if (fileName.endsWith(ext)) {
|
ResourceType prev = EXT_MAP.put(ext, type);
|
||||||
return 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;
|
return UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSupportedForUnpack(ResourceType type) {
|
|
||||||
switch (type) {
|
|
||||||
case CODE:
|
|
||||||
case ARSC:
|
|
||||||
case LIB:
|
|
||||||
case FONT:
|
|
||||||
case IMG:
|
|
||||||
case UNKNOWN:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case MANIFEST:
|
|
||||||
case XML:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,12 @@
|
|||||||
package jadx.api;
|
package jadx.api;
|
||||||
|
|
||||||
import jadx.api.ResourceFile.ZipRef;
|
|
||||||
import jadx.core.codegen.CodeWriter;
|
|
||||||
import jadx.core.utils.Utils;
|
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
|
||||||
import jadx.core.utils.files.InputFile;
|
|
||||||
import jadx.core.xmlgen.ResTableParser;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
@@ -21,149 +14,174 @@ import java.util.zip.ZipFile;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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
|
// TODO: move to core package
|
||||||
public final class ResourcesLoader {
|
public final class ResourcesLoader {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ResourcesLoader.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ResourcesLoader.class);
|
||||||
|
|
||||||
private static final int READ_BUFFER_SIZE = 8 * 1024;
|
|
||||||
private static final int LOAD_SIZE_LIMIT = 10 * 1024 * 1024;
|
|
||||||
|
|
||||||
private final JadxDecompiler jadxRef;
|
private final JadxDecompiler jadxRef;
|
||||||
|
|
||||||
ResourcesLoader(JadxDecompiler jadxRef) {
|
ResourcesLoader(JadxDecompiler jadxRef) {
|
||||||
this.jadxRef = jadxRef;
|
this.jadxRef = jadxRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ResourceFile> load(List<InputFile> inputFiles) {
|
List<ResourceFile> load() {
|
||||||
List<ResourceFile> list = new ArrayList<ResourceFile>(inputFiles.size());
|
List<File> inputFiles = jadxRef.getArgs().getInputFiles();
|
||||||
for (InputFile file : inputFiles) {
|
List<ResourceFile> list = new ArrayList<>(inputFiles.size());
|
||||||
loadFile(list, file.getFile());
|
for (File file : inputFiles) {
|
||||||
|
loadFile(list, file);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ResourceDecoder {
|
public interface ResourceDecoder<T> {
|
||||||
Object decode(long size, InputStream is) throws IOException;
|
T decode(long size, InputStream is) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object decodeStream(ResourceFile rf, ResourceDecoder decoder) throws JadxException {
|
public static <T> T decodeStream(ResourceFile rf, ResourceDecoder<T> decoder) throws JadxException {
|
||||||
ZipRef zipRef = rf.getZipRef();
|
|
||||||
if (zipRef == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ZipFile zipFile = null;
|
|
||||||
InputStream inputStream = null;
|
|
||||||
try {
|
try {
|
||||||
zipFile = new ZipFile(zipRef.getZipFile());
|
ZipRef zipRef = rf.getZipRef();
|
||||||
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
|
if (zipRef == null) {
|
||||||
if (entry == null) {
|
File file = new File(rf.getOriginalName());
|
||||||
throw new IOException("Zip entry not found: " + zipRef);
|
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
|
||||||
}
|
return decoder.decode(file.length(), inputStream);
|
||||||
inputStream = new BufferedInputStream(zipFile.getInputStream(entry));
|
|
||||||
return decoder.decode(entry.getSize(), inputStream);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new JadxException("Error decode: " + zipRef.getEntryName(), e);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (zipFile != null) {
|
|
||||||
zipFile.close();
|
|
||||||
}
|
}
|
||||||
if (inputStream != null) {
|
} else {
|
||||||
inputStream.close();
|
try (ZipFile zipFile = new ZipFile(zipRef.getZipFile())) {
|
||||||
}
|
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
|
||||||
} catch (Exception e) {
|
if (entry == null) {
|
||||||
LOG.debug("Error close zip file: {}", zipRef, e);
|
throw new IOException("Zip entry not found: " + zipRef);
|
||||||
}
|
}
|
||||||
}
|
if (!ZipSecurity.isValidZipEntry(entry)) {
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
static CodeWriter loadContent(final JadxDecompiler jadxRef, final ResourceFile rf) {
|
try (InputStream inputStream = ZipSecurity.getInputStreamForEntry(zipFile, entry)) {
|
||||||
try {
|
return decoder.decode(entry.getSize(), inputStream);
|
||||||
return (CodeWriter) decodeStream(rf, new ResourceDecoder() {
|
|
||||||
@Override
|
|
||||||
public Object decode(long size, InputStream is) throws IOException {
|
|
||||||
if (size > LOAD_SIZE_LIMIT) {
|
|
||||||
return new CodeWriter().add("File too big, size: "
|
|
||||||
+ String.format("%.2f KB", size / 1024.));
|
|
||||||
}
|
}
|
||||||
return loadContent(jadxRef, rf.getType(), is);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
} 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) {
|
} catch (JadxException e) {
|
||||||
LOG.error("Decode error", e);
|
LOG.error("Decode error", e);
|
||||||
CodeWriter cw = new CodeWriter();
|
ICodeWriter cw = jadxRef.getRoot().makeCodeWriter();
|
||||||
cw.add("Error decode ").add(rf.getType().toString().toLowerCase());
|
cw.add("Error decode ").add(rf.getType().toString().toLowerCase());
|
||||||
cw.startLine(Utils.getStackTrace(e.getCause()));
|
Utils.appendStackTrace(cw, e.getCause());
|
||||||
return cw;
|
return ResContainer.textResource(rf.getDeobfName(), cw.finish());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CodeWriter loadContent(JadxDecompiler jadxRef, ResourceType type,
|
private static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf,
|
||||||
InputStream inputStream) throws IOException {
|
InputStream inputStream) throws IOException {
|
||||||
switch (type) {
|
RootNode root = jadxRef.getRoot();
|
||||||
|
switch (rf.getType()) {
|
||||||
case MANIFEST:
|
case MANIFEST:
|
||||||
case XML:
|
case XML: {
|
||||||
return jadxRef.getXmlParser().parse(inputStream);
|
ICodeInfo content;
|
||||||
|
if (root.isProto()) {
|
||||||
|
content = jadxRef.getProtoXmlParser().parse(inputStream);
|
||||||
|
} else {
|
||||||
|
content = jadxRef.getBinaryXmlParser().parse(inputStream);
|
||||||
|
}
|
||||||
|
return ResContainer.textResource(rf.getDeobfName(), content);
|
||||||
|
}
|
||||||
|
|
||||||
case ARSC:
|
case ARSC:
|
||||||
return new ResTableParser().decodeToCodeWriter(inputStream);
|
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);
|
||||||
}
|
}
|
||||||
return loadToCodeWriter(inputStream);
|
}
|
||||||
|
|
||||||
|
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) {
|
private void loadFile(List<ResourceFile> list, File file) {
|
||||||
if (file == null) {
|
if (file == null || file.isDirectory()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ZipFile zip = null;
|
|
||||||
try {
|
// Try to load the resources with a custom loader first
|
||||||
zip = new ZipFile(file);
|
for (CustomResourcesLoader loader : jadxRef.getCustomResourcesLoaders()) {
|
||||||
Enumeration<? extends ZipEntry> entries = zip.entries();
|
if (loader.load(this, list, file)) {
|
||||||
while (entries.hasMoreElements()) {
|
LOG.debug("Custom loader used for {}", file.getAbsolutePath());
|
||||||
ZipEntry entry = entries.nextElement();
|
return;
|
||||||
addEntry(list, file, entry);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.debug("Not a zip file: {}", file.getAbsolutePath());
|
|
||||||
} finally {
|
|
||||||
if (zip != null) {
|
|
||||||
try {
|
|
||||||
zip.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.error("Zip file close error: {}", file.getAbsolutePath(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addEntry(List<ResourceFile> list, File zipFile, ZipEntry entry) {
|
public void addEntry(List<ResourceFile> list, File zipFile, ZipEntry entry, String subDir) {
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String name = entry.getName();
|
String name = entry.getName();
|
||||||
ResourceType type = ResourceType.getFileType(name);
|
ResourceType type = ResourceType.getFileType(name);
|
||||||
ResourceFile rf = new ResourceFile(jadxRef, name, type);
|
ResourceFile rf = ResourceFile.createResourceFile(jadxRef, subDir + name, type);
|
||||||
rf.setZipRef(new ZipRef(zipFile, name));
|
if (rf != null) {
|
||||||
list.add(rf);
|
rf.setZipRef(new ZipRef(zipFile, name));
|
||||||
// LOG.debug("Add resource entry: {}, size: {}", name, entry.getSize());
|
list.add(rf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CodeWriter loadToCodeWriter(InputStream is) throws IOException {
|
public static ICodeInfo loadToCodeWriter(InputStream is) throws IOException {
|
||||||
CodeWriter cw = new CodeWriter();
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(READ_BUFFER_SIZE);
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(READ_BUFFER_SIZE);
|
||||||
byte[] buffer = new byte[READ_BUFFER_SIZE];
|
copyStream(is, baos);
|
||||||
int count;
|
return new SimpleCodeInfo(baos.toString("UTF-8"));
|
||||||
try {
|
|
||||||
while ((count = is.read(buffer)) != -1) {
|
|
||||||
baos.write(buffer, 0, count);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
is.close();
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cw.add(baos.toString("UTF-8"));
|
|
||||||
return cw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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