mirror of
https://github.com/Guardsquare/proguard.git
synced 2026-03-13 09:50:34 +08:00
Compare commits
433 Commits
record-att
...
sync
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
869ce156b1 | ||
|
|
124b33e473 | ||
|
|
b8a62c8ca8 | ||
|
|
886477806c | ||
|
|
7fc907d1fb | ||
|
|
b10346ba32 | ||
|
|
f2ced20be4 | ||
|
|
35ea6d587f | ||
|
|
eea0ccbe8f | ||
|
|
bee74a9963 | ||
|
|
4b4aa93335 | ||
|
|
4781f5898f | ||
|
|
1f9a4a1b94 | ||
|
|
40f9222bc3 | ||
|
|
ef6a8352bd | ||
|
|
e225e56a8d | ||
|
|
4288cce536 | ||
|
|
3456cf330e | ||
|
|
fbcf41fd67 | ||
|
|
bacde1cede | ||
|
|
430a04502d | ||
|
|
08adfa5552 | ||
|
|
89b1e55ea2 | ||
|
|
f4c4a13a90 | ||
|
|
c1eafc7b6b | ||
|
|
73860de626 | ||
|
|
ff66baaced | ||
|
|
174d3f4155 | ||
|
|
f5352fece7 | ||
|
|
844f3d76be | ||
|
|
7b6712e840 | ||
|
|
dd4b8bde06 | ||
|
|
b3deed8286 | ||
|
|
8903bfb23f | ||
|
|
03d7effdd2 | ||
|
|
c2146ae315 | ||
|
|
4e643b4f60 | ||
|
|
ee3deb69fa | ||
|
|
6075d17bee | ||
|
|
3a9b11bb3c | ||
|
|
af475c65b4 | ||
|
|
8d7ddf898c | ||
|
|
f5f2f06334 | ||
|
|
aa43b9dc21 | ||
|
|
0d9ceb7451 | ||
|
|
1d28c11e36 | ||
|
|
b85b2cb201 | ||
|
|
0c95982828 | ||
|
|
38de2e42b2 | ||
|
|
76b2921738 | ||
|
|
7483ad32f4 | ||
|
|
20c99aa3e8 | ||
|
|
858bcd0eb5 | ||
|
|
1c421bf780 | ||
|
|
d4692c3835 | ||
|
|
712fd768ca | ||
|
|
c35913c3f2 | ||
|
|
5a8d50090a | ||
|
|
06c2d12f7a | ||
|
|
a7265a3536 | ||
|
|
7160a9e484 | ||
|
|
12c9c3f23e | ||
|
|
a02100cb93 | ||
|
|
38a0e498b9 | ||
|
|
f6b82b1478 | ||
|
|
789777ded5 | ||
|
|
e76e47953f | ||
|
|
836253f1da | ||
|
|
f92fc632b1 | ||
|
|
f5f04cbec5 | ||
|
|
ce2c8a8b5d | ||
|
|
0032aa037c | ||
|
|
0070bc9e80 | ||
|
|
6f3610bd7b | ||
|
|
813616d095 | ||
|
|
04123e8f9b | ||
|
|
7c153f8eec | ||
|
|
76cf000348 | ||
|
|
549e2cde2c | ||
|
|
f04ef27f66 | ||
|
|
bfdfa02f8c | ||
|
|
58eae9eed5 | ||
|
|
7429219cd2 | ||
|
|
8bb7cc0c4b | ||
|
|
9a7a97d1ca | ||
|
|
57d4250464 | ||
|
|
943e349f47 | ||
|
|
8afa59e7ce | ||
|
|
14673bc36b | ||
|
|
2b56fc6ced | ||
|
|
4cff876e44 | ||
|
|
1f8f548d36 | ||
|
|
09593afd21 | ||
|
|
0dd91648be | ||
|
|
1b445599e0 | ||
|
|
aa1835fb93 | ||
|
|
dda133e476 | ||
|
|
bab3379536 | ||
|
|
764d35e4d5 | ||
|
|
745a681b32 | ||
|
|
12516ba710 | ||
|
|
98309c9fbf | ||
|
|
ad82f08b10 | ||
|
|
f006f5810d | ||
|
|
dbdb423364 | ||
|
|
fbda5d5156 | ||
|
|
81561a0f2e | ||
|
|
8a7e846ec7 | ||
|
|
b9cf51b119 | ||
|
|
9b8f80229a | ||
|
|
1cbd6f7a68 | ||
|
|
3445fa02d6 | ||
|
|
4c6103eb2e | ||
|
|
47e080f526 | ||
|
|
84ac82eb51 | ||
|
|
2909a2ca7b | ||
|
|
bd98123633 | ||
|
|
c4a5a89194 | ||
|
|
3226de6d0c | ||
|
|
20e3103518 | ||
|
|
9c6eb91d01 | ||
|
|
731ffd812d | ||
|
|
72f0ef13db | ||
|
|
64c7322624 | ||
|
|
73cfa53192 | ||
|
|
34bc089656 | ||
|
|
441574cc77 | ||
|
|
1f74286f37 | ||
|
|
803b011589 | ||
|
|
da479698e9 | ||
|
|
33610e5ab0 | ||
|
|
ec3ce071bf | ||
|
|
902a7f19c3 | ||
|
|
ff74c14eba | ||
|
|
7473a042cc | ||
|
|
cf99b22895 | ||
|
|
fe293dc318 | ||
|
|
d32c159524 | ||
|
|
f75147dfc3 | ||
|
|
fe45cdfd5d | ||
|
|
bd63f2423f | ||
|
|
cf5d492b00 | ||
|
|
03c8b9f491 | ||
|
|
aca2004823 | ||
|
|
aa1936cc5f | ||
|
|
4ab73b2429 | ||
|
|
8f491a9a56 | ||
|
|
16d1b0ae87 | ||
|
|
1c7f9628bb | ||
|
|
834ba4c363 | ||
|
|
1b40f8d9cc | ||
|
|
97e501d1b9 | ||
|
|
142e7d5cbf | ||
|
|
0e2b6002c5 | ||
|
|
51a6e71bfb | ||
|
|
3f8733d4fd | ||
|
|
75b5e9854f | ||
|
|
d532ce8812 | ||
|
|
a1244e15f6 | ||
|
|
2d12f51b98 | ||
|
|
d78af60166 | ||
|
|
594507af03 | ||
|
|
3b4533faca | ||
|
|
7bd1035f37 | ||
|
|
33f70eb7f4 | ||
|
|
3ae37d6b87 | ||
|
|
eb5a49f065 | ||
|
|
c3095256e4 | ||
|
|
f16430e3b3 | ||
|
|
418828f7d4 | ||
|
|
58d9b0b2dc | ||
|
|
7a2680cebd | ||
|
|
96af8eb499 | ||
|
|
3a2c7b792e | ||
|
|
f2492965a0 | ||
|
|
04cc24fd16 | ||
|
|
3f8d78322d | ||
|
|
6ef365a05e | ||
|
|
30040ac6af | ||
|
|
376d2bc663 | ||
|
|
d6d9388b9b | ||
|
|
6851aa67a7 | ||
|
|
4982541123 | ||
|
|
b3c98ad729 | ||
|
|
ec644d5c36 | ||
|
|
78f50bba27 | ||
|
|
f8d6fe5272 | ||
|
|
4335f9cfcc | ||
|
|
e21af254d1 | ||
|
|
6fd46cc0b2 | ||
|
|
2f65030bf4 | ||
|
|
451f883fce | ||
|
|
b2e88ceed5 | ||
|
|
d91599b709 | ||
|
|
697ff01187 | ||
|
|
cfffb3e97d | ||
|
|
e937aef822 | ||
|
|
364cdc342c | ||
|
|
662db60e71 | ||
|
|
48cee42937 | ||
|
|
ccab80e755 | ||
|
|
59a74b2d51 | ||
|
|
1f786a78a5 | ||
|
|
87bb794331 | ||
|
|
d0ce5b69e6 | ||
|
|
36f6d1b929 | ||
|
|
7dc4b6c576 | ||
|
|
d62e6a7c0c | ||
|
|
cc1235c981 | ||
|
|
a1d13975f2 | ||
|
|
6bcd4a51d0 | ||
|
|
8d3c4e6b34 | ||
|
|
1035f18f65 | ||
|
|
6dc3ae5b98 | ||
|
|
609bf2d973 | ||
|
|
40fc9e24db | ||
|
|
4ae53a2152 | ||
|
|
f375d49348 | ||
|
|
ae60aad999 | ||
|
|
e534c63c6e | ||
|
|
250a51199c | ||
|
|
d9ab10c273 | ||
|
|
c85bfe4e43 | ||
|
|
30e762fcf7 | ||
|
|
587dcad79a | ||
|
|
9505a50648 | ||
|
|
dc66dadf67 | ||
|
|
0bfd2bd8ae | ||
|
|
24e3863744 | ||
|
|
1adaf23e81 | ||
|
|
30b44ccc25 | ||
|
|
96a01c32d4 | ||
|
|
1c5f806346 | ||
|
|
06cbcfcbbd | ||
|
|
972bf45a04 | ||
|
|
c7aac8af18 | ||
|
|
6b169569cc | ||
|
|
84b2994c02 | ||
|
|
0e3a3500bf | ||
|
|
dba190bf95 | ||
|
|
75b50830ff | ||
|
|
ec0d8f03ac | ||
|
|
ee0630eeed | ||
|
|
78a49d1e66 | ||
|
|
6350b26782 | ||
|
|
d651c0f4c8 | ||
|
|
3df20f6b32 | ||
|
|
9df9fcdb04 | ||
|
|
45798d2f96 | ||
|
|
d8260f892c | ||
|
|
be163ee86e | ||
|
|
d43e4c1a3c | ||
|
|
cb4127be4d | ||
|
|
2822edc08a | ||
|
|
88a57e90af | ||
|
|
0cd72ef0b5 | ||
|
|
8d351087ec | ||
|
|
2c224c1816 | ||
|
|
ada8827e25 | ||
|
|
cf23e5a80d | ||
|
|
2d012a9ee2 | ||
|
|
667cb2e1a8 | ||
|
|
b0104ecd96 | ||
|
|
3e180e5c67 | ||
|
|
c6b2bef146 | ||
|
|
0595e0e2d1 | ||
|
|
94a56c4058 | ||
|
|
121ac840a7 | ||
|
|
c4f4e3176c | ||
|
|
7b7e4aaafc | ||
|
|
a40b8326a1 | ||
|
|
b8f1552823 | ||
|
|
9896fefe16 | ||
|
|
fbdf041710 | ||
|
|
f3d9b802be | ||
|
|
eaf0443293 | ||
|
|
502fb454de | ||
|
|
e4dba1d86a | ||
|
|
2a4114cfd8 | ||
|
|
c732f3d8d2 | ||
|
|
46d080b45f | ||
|
|
50151e643c | ||
|
|
7a4e35559b | ||
|
|
61b769a5e8 | ||
|
|
a830e358ee | ||
|
|
68d0c4b725 | ||
|
|
45d78d83d6 | ||
|
|
2d806bbfad | ||
|
|
5b02980ffe | ||
|
|
2e1b0ce816 | ||
|
|
f01d3857b5 | ||
|
|
c2b8dcb869 | ||
|
|
e0200f0bfa | ||
|
|
b4ab30a5b7 | ||
|
|
806cf96d83 | ||
|
|
7c9c097fea | ||
|
|
919465a0a4 | ||
|
|
79c6b926ab | ||
|
|
ca21767df8 | ||
|
|
2cc79cb84c | ||
|
|
aeae2d3a3e | ||
|
|
71176d97da | ||
|
|
2d17ed8c8e | ||
|
|
bf8669cd92 | ||
|
|
a450a25619 | ||
|
|
e427ecce57 | ||
|
|
6dfa0ca00e | ||
|
|
29d96d4304 | ||
|
|
e0406d77c3 | ||
|
|
c77c1d7dd5 | ||
|
|
802274b03a | ||
|
|
82639a4290 | ||
|
|
d914fa94fc | ||
|
|
91e0206127 | ||
|
|
fcde4bfd92 | ||
|
|
97928776ca | ||
|
|
46b2c8b915 | ||
|
|
847683d5ee | ||
|
|
1c8d1b151c | ||
|
|
efd665afac | ||
|
|
601affb88b | ||
|
|
da524a3499 | ||
|
|
689fdaaadb | ||
|
|
5650e9e06a | ||
|
|
9c3027ad43 | ||
|
|
60da6a571e | ||
|
|
021bbb1f82 | ||
|
|
e8e749ce33 | ||
|
|
ff5f6edfdb | ||
|
|
25bcbdbc37 | ||
|
|
811cdfc6be | ||
|
|
b0f0ae6d1b | ||
|
|
898d126a38 | ||
|
|
19ec404959 | ||
|
|
bc10bcdd51 | ||
|
|
5c0478b6a1 | ||
|
|
a7462c9e77 | ||
|
|
566026eba6 | ||
|
|
de986fad86 | ||
|
|
27bf5fc814 | ||
|
|
230d3920de | ||
|
|
1a7959d978 | ||
|
|
6cf1999f52 | ||
|
|
6330b9eff9 | ||
|
|
d9c64430b4 | ||
|
|
bece91266b | ||
|
|
465304515b | ||
|
|
8b1bd44bca | ||
|
|
4d84aa543f | ||
|
|
5f69246e3a | ||
|
|
e00da102f4 | ||
|
|
036291e8fb | ||
|
|
98813c4181 | ||
|
|
e3e63607ac | ||
|
|
7ed4eb92a6 | ||
|
|
d82a4c4c13 | ||
|
|
13f824e2aa | ||
|
|
976b998c74 | ||
|
|
b4d085ce60 | ||
|
|
dd1a2583f9 | ||
|
|
9d08ab1a26 | ||
|
|
f21df09a64 | ||
|
|
9fd8c16bfb | ||
|
|
55713a9f9a | ||
|
|
87000a49ec | ||
|
|
77d734f458 | ||
|
|
b5acb79b11 | ||
|
|
cb7928d19a | ||
|
|
3db816337f | ||
|
|
97451730f3 | ||
|
|
935e7e79c0 | ||
|
|
308dec7d6e | ||
|
|
f16d3d8cb9 | ||
|
|
a58c096046 | ||
|
|
0344c58b3d | ||
|
|
bd0327e088 | ||
|
|
12fd4ae1d9 | ||
|
|
b5fbefb87c | ||
|
|
8a02ef694c | ||
|
|
26a3dd497b | ||
|
|
2ba28f1a17 | ||
|
|
af272c24b6 | ||
|
|
318efa6111 | ||
|
|
3867b50091 | ||
|
|
6afe222955 | ||
|
|
3da6b8c2a0 | ||
|
|
b5f5d666dc | ||
|
|
cd5463d12d | ||
|
|
89e1d133fc | ||
|
|
eddc8a2f87 | ||
|
|
3ea6f22730 | ||
|
|
3856c91c60 | ||
|
|
5b01532d67 | ||
|
|
75136d9aec | ||
|
|
52f110ef5d | ||
|
|
613af185f3 | ||
|
|
acacab39be | ||
|
|
8a21998eaa | ||
|
|
0cd13bd9d9 | ||
|
|
0d83884213 | ||
|
|
0fe9ab93e9 | ||
|
|
c4e9d3f56d | ||
|
|
37f55f37d2 | ||
|
|
737bf24843 | ||
|
|
f7c5ebc443 | ||
|
|
9b16748b2c | ||
|
|
97a5a9744b | ||
|
|
4302e8ceca | ||
|
|
6037875b28 | ||
|
|
868bf38e69 | ||
|
|
7547edaa83 | ||
|
|
8ce02565e6 | ||
|
|
c1f4ce0553 | ||
|
|
1213768d33 | ||
|
|
a2eb7f8f7a | ||
|
|
0cb79d8424 | ||
|
|
4db1c9c37b | ||
|
|
447f397b55 | ||
|
|
03172c0041 | ||
|
|
f8443ff522 | ||
|
|
0b89e1c148 | ||
|
|
eb54bfb1d9 | ||
|
|
a4989a00ff | ||
|
|
64d818d296 | ||
|
|
dae530fe21 | ||
|
|
912d149394 | ||
|
|
6a56cc2259 | ||
|
|
cecb95f8d0 | ||
|
|
cbbecd815a | ||
|
|
46b7300da5 | ||
|
|
679425e5d8 | ||
|
|
8532cfa2d6 | ||
|
|
c70af673c7 |
33
.github/workflows/continuous_integration.yml
vendored
33
.github/workflows/continuous_integration.yml
vendored
@@ -3,26 +3,33 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- beta
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- beta
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: proguard-main
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.PROGUARD_GITHUB_TOKEN }}
|
||||
repository: Guardsquare/proguard-core
|
||||
path: proguard-core
|
||||
- uses: actions/setup-java@v1
|
||||
fetch-depth: 0
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: 8
|
||||
- uses: eskatos/gradle-command-action@v1
|
||||
- name: Setup gradle
|
||||
uses: gradle/actions/setup-gradle@017a9effdb900e5b5b2fddfb590a105619dca3c3 # version 4.4.2
|
||||
- name: Test
|
||||
run: ./gradlew test :base:testAllJavaVersions :base:jacocoTestReport jar --info
|
||||
- name: Publish Test Report
|
||||
uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # version 5.6.2
|
||||
if: success() || failure() # always run even if the previous step fails
|
||||
with:
|
||||
build-root-directory: proguard-main/
|
||||
wrapper-directory: proguard-main/
|
||||
arguments: --include-build ../proguard-core/ jar
|
||||
report_paths: '**/build/test-results/test/TEST-*.xml'
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -4,4 +4,7 @@
|
||||
.idea
|
||||
build
|
||||
local.properties
|
||||
/lib/
|
||||
/lib/
|
||||
docs/html
|
||||
.kotlin
|
||||
.DS_Store
|
||||
|
||||
@@ -81,8 +81,7 @@ number of facade classes that construct and run these chains. Notably:
|
||||
- `Initializer` initializes links between the code and resources, to traverse
|
||||
the data structures more easily.
|
||||
|
||||
- `Marker` marks code and resources to be kept, encrypted, etc., based on the
|
||||
configuration.
|
||||
- `Marker` marks code and resources to be kept etc. based on the configuration.
|
||||
|
||||
- `Backporter` backports code to older versions of Java.
|
||||
|
||||
@@ -103,8 +102,8 @@ At a high level, the flow of data inside ProGuard is as follows:
|
||||
|
||||
- Traverse the input data, to parse any useful data structures.
|
||||
|
||||
- Process the data structures in a number of steps (mainly shrinking, string
|
||||
encryption, optimization, obfuscation, class encryption).
|
||||
- Process the data structures in a number of steps (mainly shrinking, optimization,
|
||||
obfuscation).
|
||||
|
||||
- Traverse the input data again, this time to write to the output, by copying,
|
||||
transforming, replacing, or removing data entries. The transformations can
|
||||
|
||||
168
README.md
168
README.md
@@ -1,9 +1,9 @@
|
||||
<p align="center">
|
||||
<br />
|
||||
<br />
|
||||
<a href="https://www.guardsquare.com/en/products/proguard">
|
||||
<a href="https://www.guardsquare.com/proguard">
|
||||
<img
|
||||
src="https://www.guardsquare.com/sites/default/files/media/ProGuard-RGB-1500x436.png"
|
||||
src="https://www.guardsquare.com/hubfs/Logos/ProGuard-Logo-Email.png"
|
||||
alt="ProGuard" width="400">
|
||||
</a>
|
||||
</p>
|
||||
@@ -15,11 +15,6 @@
|
||||
<img src="https://github.com/Guardsquare/proguard/workflows/Continuous%20Integration/badge.svg">
|
||||
</a>
|
||||
|
||||
<!-- jcenter -->
|
||||
<a href='https://bintray.com/guardsquare/proguard/com.guardsquare%3Aproguard-base/_latestVersion'>
|
||||
<img alt="jcenter" src="https://img.shields.io/bintray/v/guardsquare/proguard/com.guardsquare:proguard-base?label=jcenter">
|
||||
</a>
|
||||
|
||||
<!-- Github version -->
|
||||
<!--
|
||||
<a href="releases">
|
||||
@@ -28,11 +23,9 @@
|
||||
-->
|
||||
|
||||
<!-- Maven -->
|
||||
<!--
|
||||
<a href="https://search.maven.org/search?q=g:com.guardsquare">
|
||||
<img src="https://img.shields.io/maven-central/v/com.guardsquare/proguard-parent">
|
||||
<img src="https://img.shields.io/maven-central/v/com.guardsquare/proguard-base">
|
||||
</a>
|
||||
-->
|
||||
|
||||
<!-- License -->
|
||||
<a href="LICENSE">
|
||||
@@ -64,61 +57,88 @@ bytecode:
|
||||
* It renames the remaining classes, fields, and methods using short
|
||||
meaningless names.
|
||||
|
||||
The resulting applications and libraries are smaller, faster, and a bit better
|
||||
hardened against reverse engineering. ProGuard is very popular for Android
|
||||
development, but it also works for Java code in general.
|
||||
The resulting applications and libraries are smaller and faster.
|
||||
|
||||
## ❓ Getting Help
|
||||
Please use <a href="https://github.com/guardsquare/proguard/issues">**the issue tracker**</a> to report actual **bugs 🐛, crashes**, etc.
|
||||
<br />
|
||||
<br />
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
ProGuard is integrated in Google's Android SDK. If you have an Android Gradle
|
||||
project you can enable ProGuard instead of the default R8 compiler:
|
||||
### Command line
|
||||
|
||||
1. Disable R8 in your `gradle.properties`:
|
||||
```
|
||||
android.enableR8=false
|
||||
android.enableR8.libraries=false
|
||||
```
|
||||
First, download the latest release from [GitHub releases](https://github.com/Guardsquare/proguard/releases).
|
||||
|
||||
2. Override the default version of ProGuard with the most recent one in your
|
||||
main `build.gradle`:
|
||||
```
|
||||
buildscript {
|
||||
...
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
dependencySubstitution {
|
||||
substitute module('net.sf.proguard:proguard-gradle') with module('com.guardsquare:proguard-gradle:7.0.0')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
To run ProGuard, on Linux/MacOS, just type:
|
||||
|
||||
3. Enable minification as usual in your `build.gradle`:
|
||||
```
|
||||
android {
|
||||
...
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
|
||||
proguardFile 'proguard-project.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. Add any necessary configuration to your `proguard-project.txt`.
|
||||
|
||||
You can then build your application as usual:
|
||||
```
|
||||
gradle assembleRelease
|
||||
```bash
|
||||
bin/proguard.sh <options...>
|
||||
```
|
||||
|
||||
The repository contains some sample configurations in the [examples](examples)
|
||||
directory. Notably, [examples/android](examples/android) has a small working
|
||||
Android project.
|
||||
or on Windows:
|
||||
|
||||
```
|
||||
bin\proguard.bat <options...>
|
||||
```
|
||||
|
||||
Typically, you'll put most options in a configuration file (say,
|
||||
`myconfig.pro`), and just call
|
||||
|
||||
```bash
|
||||
bin/proguard.sh @myconfig.pro
|
||||
```
|
||||
or on Windows:
|
||||
|
||||
```
|
||||
bin\proguard.bat @myconfig.pro
|
||||
```
|
||||
|
||||
All available options are described in the [configuration section of the manual](https://www.guardsquare.com/manual/configuration/usage).
|
||||
|
||||
### Gradle Task
|
||||
|
||||
ProGuard can be run as a task in Gradle. Before you can use the proguard task, you have to make sure Gradle can
|
||||
find it in its class path at build time. One way is to add the following
|
||||
line to your **`build.gradle`** file which will download ProGuard from Maven Central:
|
||||
|
||||
```Groovy
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.guardsquare:proguard-gradle:7.8.0'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can then define a task with configuration:
|
||||
|
||||
```Groovy
|
||||
tasks.register('proguard', ProGuardTask) {
|
||||
configuration file('proguard.pro')
|
||||
|
||||
injars(tasks.named('jar', Jar).flatMap { it.archiveFile })
|
||||
|
||||
// Automatically handle the Java version of this build.
|
||||
if (System.getProperty('java.version').startsWith('1.')) {
|
||||
// Before Java 9, the runtime classes were packaged in a single jar file.
|
||||
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
|
||||
} else {
|
||||
// As of Java 9, the runtime classes are packaged in modular jmod files.
|
||||
libraryjars "${System.getProperty('java.home')}/jmods/java.base.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
|
||||
//libraryjars "${System.getProperty('java.home')}/jmods/....."
|
||||
}
|
||||
|
||||
verbose
|
||||
|
||||
outjars(layout.buildDirectory.file("libs/${baseCoordinates}-minified.jar"))
|
||||
}
|
||||
```
|
||||
|
||||
The embedded configuration is much like a standard ProGuard
|
||||
configuration. You can find more details on the [Gradle setup page](https://www.guardsquare.com/manual/setup/gradle).
|
||||
|
||||
## ✨ Features
|
||||
|
||||
@@ -132,8 +152,7 @@ inlining methods, propagating constants, removing unused parameters, etc.
|
||||
|
||||
* The optimizations may also improve the performance of the application, by up
|
||||
to 20%. For Java virtual machines on servers and desktops, the difference
|
||||
generally isn't noticeable. For the Dalvik virtual machine and ART on
|
||||
Android devices, the difference can be worth it.
|
||||
generally isn't noticeable.
|
||||
|
||||
* ProGuard can also remove logging code, from applications and their
|
||||
libraries, without needing to change the source code — in fact,
|
||||
@@ -143,18 +162,37 @@ The manual pages ([markdown](docs/md),
|
||||
[html](https://www.guardsquare.com/proguard/manual)) cover the features and usage of
|
||||
ProGuard in detail.
|
||||
|
||||
## 💻 Building ProGuard
|
||||
|
||||
Building ProGuard is easy - you'll just need a Java 8 JDK installed.
|
||||
To build from source, clone a copy of the ProGuard repository and run the following command:
|
||||
|
||||
```bash
|
||||
./gradlew assemble
|
||||
```
|
||||
|
||||
The artifacts will be generated in the `lib` directory. You can then execute ProGuard using the
|
||||
scripts in `bin`, for example:
|
||||
|
||||
```bash
|
||||
bin/proguard.sh
|
||||
```
|
||||
|
||||
You can publish the artifacts to your local Maven repository using:
|
||||
|
||||
```bash
|
||||
./gradlew publishToMavenLocal
|
||||
```
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
ProGuard builds on the
|
||||
[ProGuard Core](https://github.com/Guardsquare/proguard-core) library.
|
||||
|
||||
Contributions, issues and feature requests are welcome in both projects.
|
||||
Feel free to check the [issues](issues) page and the [contributing
|
||||
Feel free to check the [issues](https://github.com/Guardsquare/proguard/issues) page and the [contributing
|
||||
guide](CONTRIBUTING.md) if you would like to contribute.
|
||||
|
||||
## 📝 License
|
||||
|
||||
Copyright (c) 2002-2020 [Guardsquare NV](https://www.guardsquare.com/).
|
||||
Copyright (c) 2002-2025 [Guardsquare NV](https://www.guardsquare.com/).
|
||||
ProGuard is released under the [GNU General Public License, version
|
||||
2](LICENSE), with [exceptions granted to a number of
|
||||
projects](docs/md/license.md).
|
||||
projects](docs/md/manual/license/gplexception.md).
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
plugins {
|
||||
id 'com.jfrog.bintray'
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
sourceCompatibility = "${target}"
|
||||
targetCompatibility = "${target}"
|
||||
|
||||
sourceSets.main {
|
||||
java {
|
||||
srcDirs = ['src']
|
||||
}
|
||||
resources {
|
||||
srcDirs = ['src']
|
||||
include '**/*.properties'
|
||||
include '**/*.gif'
|
||||
include '**/*.png'
|
||||
include '**/*.pro'
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications.getByName(project.name) {
|
||||
pom {
|
||||
description = 'Java annotations to configure ProGuard, the free shrinker, optimizer, obfuscator, and preverifier for Java bytecode'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,41 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
plugins {
|
||||
id 'com.jfrog.bintray'
|
||||
id 'com.github.johnrengelman.shadow'
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
sourceCompatibility = "${target}"
|
||||
targetCompatibility = "${target}"
|
||||
|
||||
sourceSets.main {
|
||||
java {
|
||||
srcDirs = ['src']
|
||||
}
|
||||
resources {
|
||||
srcDirs = ['src']
|
||||
include '**/*.properties'
|
||||
include '**/*.gif'
|
||||
include '**/*.png'
|
||||
include '**/*.pro'
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':base')
|
||||
compile 'com.guardsquare:proguard-core'
|
||||
compile 'org.apache.ant:ant:1.9.7'
|
||||
implementation project(':base')
|
||||
implementation 'org.apache.ant:ant:1.10.15'
|
||||
}
|
||||
|
||||
task fatJar(type: ShadowJar) {
|
||||
destinationDirectory.set(file("$rootDir/lib"))
|
||||
archiveFileName.set('proguard-ant.jar')
|
||||
from sourceSets.main.output
|
||||
configurations = [project.configurations.runtimeClasspath]
|
||||
manifest {
|
||||
attributes(
|
||||
'Manifest-Version': '1.0',
|
||||
'Multi-Release': true,
|
||||
'Implementation-Version': archiveVersion.get())
|
||||
}
|
||||
}
|
||||
|
||||
assemble.dependsOn fatJar
|
||||
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications.getByName(project.name) {
|
||||
pom {
|
||||
description = 'Ant plugin for ProGuard, the free shrinker, optimizer, obfuscator, and preverifier for Java bytecode'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +99,8 @@ public class ConfigurationElement extends FileSet
|
||||
{
|
||||
File configurationFile = new File(baseDir, fileNames[index]);
|
||||
|
||||
ConfigurationParser parser =
|
||||
new ConfigurationParser(configurationFile, properties);
|
||||
try
|
||||
try (ConfigurationParser parser =
|
||||
new ConfigurationParser(configurationFile, properties))
|
||||
{
|
||||
parser.parse(configuration);
|
||||
}
|
||||
@@ -109,10 +108,6 @@ public class ConfigurationElement extends FileSet
|
||||
{
|
||||
throw new BuildException(ex.getMessage());
|
||||
}
|
||||
finally
|
||||
{
|
||||
parser.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
@@ -347,12 +347,10 @@ public class ConfigurationTask extends Task
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(project.getProperties());
|
||||
|
||||
ConfigurationParser parser = new ConfigurationParser(arg,
|
||||
"embedded configuration",
|
||||
project.getBaseDir(),
|
||||
properties);
|
||||
|
||||
try
|
||||
try (ConfigurationParser parser = new ConfigurationParser(arg,
|
||||
"embedded configuration",
|
||||
project.getBaseDir(),
|
||||
properties))
|
||||
{
|
||||
parser.parse(configuration);
|
||||
}
|
||||
@@ -360,10 +358,6 @@ public class ConfigurationTask extends Task
|
||||
{
|
||||
throw new BuildException(ex.getMessage());
|
||||
}
|
||||
finally
|
||||
{
|
||||
parser.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
@@ -49,11 +49,9 @@ public class ProGuardTask extends ConfigurationTask
|
||||
URL configUrl =
|
||||
ConfigurationElement.class.getResource(configurationFile.toString());
|
||||
|
||||
ConfigurationParser parser = configUrl != null ?
|
||||
new ConfigurationParser(configUrl, properties) :
|
||||
new ConfigurationParser(configurationFile, properties);
|
||||
|
||||
try
|
||||
try (ConfigurationParser parser = configUrl != null ?
|
||||
new ConfigurationParser(configUrl, properties) :
|
||||
new ConfigurationParser(configurationFile, properties))
|
||||
{
|
||||
parser.parse(configuration);
|
||||
}
|
||||
@@ -61,10 +59,6 @@ public class ProGuardTask extends ConfigurationTask
|
||||
{
|
||||
throw new BuildException(e.getMessage(), e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
parser.close();
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
@@ -338,7 +332,7 @@ public class ProGuardTask extends ConfigurationTask
|
||||
ProGuard proGuard = new ProGuard(configuration);
|
||||
proGuard.execute();
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new BuildException(e.getMessage(), e);
|
||||
}
|
||||
@@ -1,49 +1,104 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
plugins {
|
||||
id 'com.github.johnrengelman.shadow'
|
||||
id 'com.jfrog.bintray'
|
||||
id 'java'
|
||||
id 'java-library'
|
||||
id 'java-test-fixtures'
|
||||
id 'maven-publish'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id 'com.adarshr.test-logger' version '4.0.0'
|
||||
id 'jacoco'
|
||||
id "org.jlleitschuh.gradle.ktlint" version '12.1.2'
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
sourceCompatibility = "${target}"
|
||||
targetCompatibility = "${target}"
|
||||
|
||||
sourceSets.main {
|
||||
java {
|
||||
srcDirs = ['src']
|
||||
}
|
||||
resources {
|
||||
srcDirs = ['src']
|
||||
include '**/*.properties'
|
||||
include '**/*.gif'
|
||||
include '**/*.png'
|
||||
include '**/*.pro'
|
||||
}
|
||||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
|
||||
kotlinOptions {
|
||||
jvmTarget = "${target}"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.guardsquare:proguard-core:7.0.0'
|
||||
compile "com.google.code.gson:gson:${gsonVersion}"
|
||||
api "com.guardsquare:proguard-core:${proguardCoreVersion}"
|
||||
implementation "com.google.code.gson:gson:${gsonVersion}"
|
||||
implementation 'org.apache.logging.log4j:log4j-api:2.24.2'
|
||||
implementation 'org.apache.logging.log4j:log4j-core:2.24.2'
|
||||
implementation 'org.json:json:20231013'
|
||||
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
testImplementation 'dev.zacsweers.kctfork:core:0.6.0'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1' // for kotest framework
|
||||
testImplementation 'io.kotest:kotest-assertions-core-jvm:5.9.1' // for kotest core jvm assertions
|
||||
testImplementation 'io.kotest:kotest-property-jvm:5.9.1' // for kotest property test
|
||||
testImplementation 'io.mockk:mockk:1.13.13' // for mocking
|
||||
|
||||
testImplementation(testFixtures("com.guardsquare:proguard-core:${proguardCoreVersion}")) {
|
||||
exclude group: 'com.guardsquare', module: 'proguard-core'
|
||||
}
|
||||
}
|
||||
|
||||
jar.manifest.attributes('Implementation-Version': version)
|
||||
|
||||
task fatJar(type: ShadowJar) {
|
||||
destinationDirectory.set(file("$rootDir/lib"))
|
||||
archiveFileName.set('proguard.jar')
|
||||
from sourceSets.main.output
|
||||
configurations = [project.configurations.runtime]
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
'Main-Class': 'proguard.ProGuard',
|
||||
'Multi-Release': true,
|
||||
'Implementation-Version': archiveVersion.get())
|
||||
}
|
||||
}
|
||||
|
||||
assemble.dependsOn fatJar
|
||||
// Early access automatic downloads are not yet supported:
|
||||
// https://github.com/gradle/gradle/issues/14814
|
||||
// But it will work if e.g. Java N-ea is pre-installed
|
||||
def javaVersionsForTest = 9..23
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
task testAllJavaVersions() { testAllTask ->
|
||||
dependsOn(test) // the usual test runs on Java 8
|
||||
|
||||
javaVersionsForTest.each {version ->
|
||||
task("testJava$version", type: Test) {
|
||||
useJUnitPlatform()
|
||||
ignoreFailures = true
|
||||
|
||||
// The version of bytebuddy used by mockk only supports Java 22 experimentally so far
|
||||
// if (version >= 22) systemProperty 'net.bytebuddy.experimental', true
|
||||
|
||||
testAllTask.dependsOn(it)
|
||||
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
// Define which classes need to be monitored
|
||||
def sources = files(project.sourceSets.main.allSource.srcDirs)
|
||||
sourceDirectories.setFrom(sources)
|
||||
additionalSourceDirs.setFrom(sources)
|
||||
sourceDirectories.setFrom(sources)
|
||||
def classes = files(project.sourceSets.main.output.classesDirs)
|
||||
classDirectories.setFrom(classes)
|
||||
executionData.setFrom project.fileTree(dir: '.', include: '**/build/jacoco/*.exec')
|
||||
reports {
|
||||
xml.required = true
|
||||
csv.required = false
|
||||
}
|
||||
javaVersionsForTest.each { version ->
|
||||
mustRunAfter "testJava$version"
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications.getByName(project.name) {
|
||||
pom {
|
||||
description = 'ProGuard is a free shrinker, optimizer, obfuscator, and preverifier for Java bytecode'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2022 Guardsquare NV
|
||||
*/
|
||||
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.classfile.visitor.ClassVisitor;
|
||||
import proguard.pass.Pass;
|
||||
|
||||
/**
|
||||
* This pass performs configuration checks for which class pools or resource information
|
||||
* should already have been initialized.
|
||||
*/
|
||||
public class AfterInitConfigurationVerifier implements Pass
|
||||
{
|
||||
private final Configuration configuration;
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(AfterInitConfigurationVerifier.class);
|
||||
|
||||
public AfterInitConfigurationVerifier(Configuration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(AppView appView)
|
||||
{
|
||||
if (configuration.targetClassVersion != 0)
|
||||
{
|
||||
// Fail if -target is set and program class pool contains a class with class version > 11.
|
||||
appView.programClassPool.classesAccept(new BackportMaxVersionVisitor(VersionConstants.CLASS_VERSION_11,
|
||||
configuration.targetClassVersion));
|
||||
}
|
||||
}
|
||||
|
||||
private static class BackportMaxVersionVisitor implements ClassVisitor {
|
||||
|
||||
private final int maxClassFileVersion;
|
||||
|
||||
private final int target;
|
||||
|
||||
private BackportMaxVersionVisitor(int maxClassFileVersion, int target)
|
||||
{
|
||||
this.maxClassFileVersion = maxClassFileVersion;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
// Implementations of ClassVisitor.
|
||||
|
||||
@Override
|
||||
public void visitProgramClass(ProgramClass programClass)
|
||||
{
|
||||
if (programClass.u4version > maxClassFileVersion)
|
||||
{
|
||||
if (programClass.u4version != target)
|
||||
{
|
||||
throw new RuntimeException("-target can only be used with class file versions <= " + ClassUtil.internalMajorClassVersion(maxClassFileVersion) +
|
||||
" (Java " + ClassUtil.externalClassVersion(maxClassFileVersion) + ")." + System.lineSeparator() +
|
||||
"The input classes contain version " + ClassUtil.internalMajorClassVersion(programClass.u4version) +
|
||||
" class files which cannot be backported to target version (" + ClassUtil.internalMajorClassVersion(target) + ").");
|
||||
}
|
||||
|
||||
logger.warn(
|
||||
"-target is deprecated when using class file above "+ ClassUtil.internalMajorClassVersion(maxClassFileVersion) +
|
||||
" (Java " + ClassUtil.externalClassVersion(maxClassFileVersion) + ")."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAnyClass(Clazz clazz)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
49
base/src/main/java/proguard/AppView.java
Normal file
49
base/src/main/java/proguard/AppView.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*/
|
||||
|
||||
package proguard;
|
||||
|
||||
import proguard.classfile.*;
|
||||
import proguard.configuration.InitialStateInfo;
|
||||
import proguard.io.ExtraDataEntryNameMap;
|
||||
import proguard.resources.file.ResourceFilePool;
|
||||
|
||||
public class AppView
|
||||
{
|
||||
// App model.
|
||||
public final ClassPool programClassPool;
|
||||
public final ClassPool libraryClassPool;
|
||||
public final ResourceFilePool resourceFilePool;
|
||||
|
||||
public final ExtraDataEntryNameMap extraDataEntryNameMap;
|
||||
|
||||
/**
|
||||
* Stores information about the original state of the program class pool used for configuration debugging.
|
||||
*/
|
||||
public InitialStateInfo initialStateInfo;
|
||||
|
||||
public AppView(ClassPool programClassPool, ClassPool libraryClassPool)
|
||||
{
|
||||
this(programClassPool, libraryClassPool, new ResourceFilePool(), new ExtraDataEntryNameMap());
|
||||
}
|
||||
|
||||
public AppView()
|
||||
{
|
||||
this(new ClassPool(), new ClassPool(), new ResourceFilePool(), new ExtraDataEntryNameMap());
|
||||
}
|
||||
|
||||
public AppView(ClassPool programClassPool,
|
||||
ClassPool libraryClassPool,
|
||||
ResourceFilePool resourceFilePool,
|
||||
ExtraDataEntryNameMap extraDataEntryNameMap)
|
||||
{
|
||||
this.programClassPool = programClassPool;
|
||||
this.resourceFilePool = resourceFilePool;
|
||||
this.libraryClassPool = libraryClassPool;
|
||||
this.extraDataEntryNameMap = extraDataEntryNameMap;
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,12 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.util.*;
|
||||
import proguard.classfile.visitor.MemberVisitor;
|
||||
import proguard.optimize.info.ReadWriteFieldMarker;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -34,6 +37,7 @@ import java.util.List;
|
||||
public class ClassMemberChecker
|
||||
implements MemberVisitor
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(ClassMemberChecker.class);
|
||||
private final ClassPool programClassPool;
|
||||
private final WarningPrinter notePrinter;
|
||||
|
||||
@@ -144,14 +148,14 @@ implements MemberVisitor
|
||||
|
||||
public void visitProgramField(ProgramClass programClass, ProgramField programField)
|
||||
{
|
||||
System.out.println(" Maybe you meant the field '" +
|
||||
ClassUtil.externalFullFieldDescription(0, programField.getName(programClass), programField.getDescriptor(programClass)) + "'?");
|
||||
logger.info(" Maybe you meant the field '{}'?",
|
||||
ClassUtil.externalFullFieldDescription(0, programField.getName(programClass), programField.getDescriptor(programClass)));
|
||||
}
|
||||
|
||||
|
||||
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
|
||||
{
|
||||
System.out.println(" Maybe you meant the method '" +
|
||||
ClassUtil.externalFullMethodDescription(programClass.getName(), 0, programMethod.getName(programClass), programMethod.getDescriptor(programClass)) + "'?");
|
||||
logger.info(" Maybe you meant the method '{}'?",
|
||||
ClassUtil.externalFullMethodDescription(programClass.getName(), 0, programMethod.getName(programClass), programMethod.getDescriptor(programClass)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
* Copyright (c) 2002-2022 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -41,18 +41,18 @@ import java.util.List;
|
||||
*/
|
||||
public class ClassPathEntry
|
||||
{
|
||||
private File file;
|
||||
private boolean output;
|
||||
private String featureName;
|
||||
private List filter;
|
||||
private List apkFilter;
|
||||
private List aabFilter;
|
||||
private List jarFilter;
|
||||
private List aarFilter;
|
||||
private List warFilter;
|
||||
private List earFilter;
|
||||
private List jmodFilter;
|
||||
private List zipFilter;
|
||||
private File file;
|
||||
private boolean output;
|
||||
private String featureName;
|
||||
private List<String> filter;
|
||||
private List<String> apkFilter;
|
||||
private List<String> aabFilter;
|
||||
private List<String> jarFilter;
|
||||
private List<String> aarFilter;
|
||||
private List<String> warFilter;
|
||||
private List<String> earFilter;
|
||||
private List<String> jmodFilter;
|
||||
private List<String> zipFilter;
|
||||
|
||||
private String cachedName;
|
||||
|
||||
@@ -289,7 +289,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Returns the name filter that is applied to bottom-level files in this entry.
|
||||
*/
|
||||
public List getFilter()
|
||||
public List<String> getFilter()
|
||||
{
|
||||
return filter;
|
||||
}
|
||||
@@ -297,7 +297,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Sets the name filter that is applied to bottom-level files in this entry.
|
||||
*/
|
||||
public void setFilter(List filter)
|
||||
public void setFilter(List<String> filter)
|
||||
{
|
||||
this.filter = filter == null || filter.size() == 0 ? null : filter;
|
||||
}
|
||||
@@ -306,7 +306,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Returns the name filter that is applied to apk files in this entry, if any.
|
||||
*/
|
||||
public List getApkFilter()
|
||||
public List<String> getApkFilter()
|
||||
{
|
||||
return apkFilter;
|
||||
}
|
||||
@@ -314,7 +314,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Sets the name filter that is applied to apk files in this entry, if any.
|
||||
*/
|
||||
public void setApkFilter(List filter)
|
||||
public void setApkFilter(List<String> filter)
|
||||
{
|
||||
this.apkFilter = filter == null || filter.size() == 0 ? null : filter;
|
||||
}
|
||||
@@ -323,7 +323,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Returns the name filter that is applied to aab files in this entry, if any.
|
||||
*/
|
||||
public List getAabFilter()
|
||||
public List<String> getAabFilter()
|
||||
{
|
||||
return aabFilter;
|
||||
}
|
||||
@@ -331,7 +331,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Sets the name filter that is applied to aab files in this entry, if any.
|
||||
*/
|
||||
public void setAabFilter(List filter)
|
||||
public void setAabFilter(List<String> filter)
|
||||
{
|
||||
this.aabFilter = filter == null || filter.size() == 0 ? null : filter;
|
||||
}
|
||||
@@ -340,7 +340,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Returns the name filter that is applied to jar files in this entry, if any.
|
||||
*/
|
||||
public List getJarFilter()
|
||||
public List<String> getJarFilter()
|
||||
{
|
||||
return jarFilter;
|
||||
}
|
||||
@@ -348,7 +348,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Sets the name filter that is applied to jar files in this entry, if any.
|
||||
*/
|
||||
public void setJarFilter(List filter)
|
||||
public void setJarFilter(List<String> filter)
|
||||
{
|
||||
this.jarFilter = filter == null || filter.size() == 0 ? null : filter;
|
||||
}
|
||||
@@ -357,7 +357,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Returns the name filter that is applied to aar files in this entry, if any.
|
||||
*/
|
||||
public List getAarFilter()
|
||||
public List<String> getAarFilter()
|
||||
{
|
||||
return aarFilter;
|
||||
}
|
||||
@@ -365,7 +365,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Sets the name filter that is applied to aar files in this entry, if any.
|
||||
*/
|
||||
public void setAarFilter(List filter)
|
||||
public void setAarFilter(List<String> filter)
|
||||
{
|
||||
this.aarFilter = filter == null || filter.size() == 0 ? null : filter;
|
||||
}
|
||||
@@ -374,7 +374,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Returns the name filter that is applied to war files in this entry, if any.
|
||||
*/
|
||||
public List getWarFilter()
|
||||
public List<String> getWarFilter()
|
||||
{
|
||||
return warFilter;
|
||||
}
|
||||
@@ -382,7 +382,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Sets the name filter that is applied to war files in this entry, if any.
|
||||
*/
|
||||
public void setWarFilter(List filter)
|
||||
public void setWarFilter(List<String> filter)
|
||||
{
|
||||
this.warFilter = filter == null || filter.size() == 0 ? null : filter;
|
||||
}
|
||||
@@ -391,7 +391,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Returns the name filter that is applied to ear files in this entry, if any.
|
||||
*/
|
||||
public List getEarFilter()
|
||||
public List<String> getEarFilter()
|
||||
{
|
||||
return earFilter;
|
||||
}
|
||||
@@ -399,7 +399,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Sets the name filter that is applied to ear files in this entry, if any.
|
||||
*/
|
||||
public void setEarFilter(List filter)
|
||||
public void setEarFilter(List<String> filter)
|
||||
{
|
||||
this.earFilter = filter == null || filter.size() == 0 ? null : filter;
|
||||
}
|
||||
@@ -408,7 +408,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Returns the name filter that is applied to jmod files in this entry, if any.
|
||||
*/
|
||||
public List getJmodFilter()
|
||||
public List<String> getJmodFilter()
|
||||
{
|
||||
return jmodFilter;
|
||||
}
|
||||
@@ -416,7 +416,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Sets the name filter that is applied to jmod files in this entry, if any.
|
||||
*/
|
||||
public void setJmodFilter(List filter)
|
||||
public void setJmodFilter(List<String> filter)
|
||||
{
|
||||
this.jmodFilter = filter == null || filter.size() == 0 ? null : filter;
|
||||
}
|
||||
@@ -424,7 +424,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Returns the name filter that is applied to zip files in this entry, if any.
|
||||
*/
|
||||
public List getZipFilter()
|
||||
public List<String> getZipFilter()
|
||||
{
|
||||
return zipFilter;
|
||||
}
|
||||
@@ -432,7 +432,7 @@ public class ClassPathEntry
|
||||
/**
|
||||
* Sets the name filter that is applied to zip files in this entry, if any.
|
||||
*/
|
||||
public void setZipFilter(List filter)
|
||||
public void setZipFilter(List<String> filter)
|
||||
{
|
||||
this.zipFilter = filter == null || filter.size() == 0 ? null : filter;
|
||||
}
|
||||
@@ -41,9 +41,9 @@ public class ClassSpecification implements Cloneable
|
||||
public final String extendsAnnotationType;
|
||||
public final String extendsClassName;
|
||||
|
||||
public final List attributeNames = null;
|
||||
public List fieldSpecifications;
|
||||
public List methodSpecifications;
|
||||
public final List<String> attributeNames = null;
|
||||
public List<MemberSpecification> fieldSpecifications;
|
||||
public List<MemberSpecification> methodSpecifications;
|
||||
|
||||
|
||||
/**
|
||||
@@ -235,11 +235,22 @@ public class ClassSpecificationVisitorFactory
|
||||
}
|
||||
}
|
||||
|
||||
// If specified, visit a single named class, otherwise visit all classes.
|
||||
return className != null ?
|
||||
new NamedClassVisitor(combinedClassVisitor, className) :
|
||||
new AllClassVisitor(combinedClassVisitor);
|
||||
}
|
||||
return
|
||||
// If specified, visit a single named class.
|
||||
className != null ?
|
||||
new NamedClassVisitor(combinedClassVisitor, className) :
|
||||
|
||||
// If an extendsClassName is specified, start visiting from matching extendsClassName classes.
|
||||
extendsClassName != null ?
|
||||
new FilteredClassVisitor(extendsClassNameMatcher, combinedClassVisitor) :
|
||||
|
||||
// If there is a className filter, start visiting from matching className classes.
|
||||
classSpecification.className != null ?
|
||||
new FilteredClassVisitor(classNameMatcher, combinedClassVisitor) :
|
||||
|
||||
// Otherwise, visit all classes.
|
||||
new AllClassVisitor(combinedClassVisitor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -418,9 +418,27 @@ public class Configuration
|
||||
*/
|
||||
public boolean keepKotlinMetadata = false;
|
||||
|
||||
/**
|
||||
* Specifies not to process Kotlin metadata. Overwrites KeepKotlinMetadata.
|
||||
*/
|
||||
public boolean dontProcessKotlinMetadata = false;
|
||||
|
||||
// INTERNAL OPTIONS
|
||||
|
||||
/**
|
||||
* Enables of disables the Kotlin asserter. This is a hidden option,
|
||||
* not available via the configuration file.
|
||||
*/
|
||||
public boolean enableKotlinAsserter = true;
|
||||
|
||||
/**
|
||||
* File to write extra data entries to; instead of writing them to
|
||||
* their respective jars. See {@link proguard.io.ExtraDataEntryNameMap}.
|
||||
*/
|
||||
public File extraJar;
|
||||
|
||||
/**
|
||||
* Specifies whether conservative optimization should be applied
|
||||
*/
|
||||
public boolean optimizeConservatively = true;
|
||||
}
|
||||
@@ -113,6 +113,13 @@ public class ConfigurationConstants
|
||||
public static final String FORCE_PROCESSING_OPTION = "-forceprocessing";
|
||||
|
||||
public static final String KEEP_KOTLIN_METADATA = "-keepkotlinmetadata";
|
||||
public static final String DONT_PROCESS_KOTLIN_METADATA = "-dontprocesskotlinmetadata";
|
||||
public static final String OPTIMIZE_AGGRESSIVELY = "-optimizeaggressively";
|
||||
|
||||
public static final String ALWAYS_INLINE = "-alwaysinline";
|
||||
public static final String IDENTIFIER_NAME_STRING = "-identifiernamestring";
|
||||
public static final String MAXIMUM_REMOVED_ANDROID_LOG_LEVEL = "-maximumremovedandroidloglevel";
|
||||
|
||||
|
||||
public static final String ANY_FILE_KEYWORD = "**";
|
||||
|
||||
@@ -20,14 +20,25 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.AccessConstants;
|
||||
import proguard.classfile.ClassConstants;
|
||||
import proguard.classfile.JavaAccessConstants;
|
||||
import proguard.classfile.JavaTypeConstants;
|
||||
import proguard.classfile.TypeConstants;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.util.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import proguard.util.ListUtil;
|
||||
import proguard.util.StringUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.StringReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* This class parses ProGuard configurations. Configurations can be read from an
|
||||
@@ -36,8 +47,10 @@ import java.util.*;
|
||||
*
|
||||
* @author Eric Lafortune
|
||||
*/
|
||||
public class ConfigurationParser
|
||||
public class ConfigurationParser implements AutoCloseable
|
||||
{
|
||||
private final boolean useDalvikVerification = System.getProperty("proguard.use.dalvik.identifier.verification") != null;
|
||||
|
||||
private final WordReader reader;
|
||||
private final Properties properties;
|
||||
|
||||
@@ -139,9 +152,24 @@ public class ConfigurationParser
|
||||
* @throws IOException if an IO error occurs while reading a configuration.
|
||||
*/
|
||||
public void parse(Configuration configuration)
|
||||
throws ParseException, IOException {
|
||||
parse(configuration, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and returns the configuration.
|
||||
*
|
||||
* @param configuration the configuration that is updated as a side-effect.
|
||||
* @param unknownOptionHandler optional handler for unknown options; if null then a {@link ParseException}
|
||||
* is thrown when encountering an unknown option.
|
||||
* @throws ParseException if the any of the configuration settings contains
|
||||
* a syntax error.
|
||||
* @throws IOException if an IO error occurs while reading a configuration.
|
||||
*/
|
||||
public void parse(Configuration configuration, BiConsumer<String, String> unknownOptionHandler)
|
||||
throws ParseException, IOException
|
||||
{
|
||||
while (nextWord != null)
|
||||
parseWord: while (nextWord != null)
|
||||
{
|
||||
lastComments = reader.lastComments();
|
||||
|
||||
@@ -210,7 +238,8 @@ public class ConfigurationParser
|
||||
else if (ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION .startsWith(nextWord)) configuration.adaptClassStrings = parseCommaSeparatedList("class name", true, true, false, false, true, false, false, true, false, configuration.adaptClassStrings);
|
||||
else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION .startsWith(nextWord)) configuration.adaptResourceFileNames = parseCommaSeparatedList("resource file name", true, true, false, true, false, true, false, false, false, configuration.adaptResourceFileNames);
|
||||
else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION .startsWith(nextWord)) configuration.adaptResourceFileContents = parseCommaSeparatedList("resource file name", true, true, false, true, false, true, false, false, false, configuration.adaptResourceFileContents);
|
||||
else if (ConfigurationConstants.KEEP_KOTLIN_METADATA .startsWith(nextWord)) configuration.keepKotlinMetadata = parseNoArgument(true);
|
||||
else if (ConfigurationConstants.DONT_PROCESS_KOTLIN_METADATA .startsWith(nextWord)) configuration.dontProcessKotlinMetadata = parseNoArgument(true);
|
||||
else if (ConfigurationConstants.KEEP_KOTLIN_METADATA .startsWith(nextWord)) configuration.keepKotlinMetadata = parseKeepKotlinMetadata();
|
||||
|
||||
else if (ConfigurationConstants.DONT_PREVERIFY_OPTION .startsWith(nextWord)) configuration.preverify = parseNoArgument(false);
|
||||
else if (ConfigurationConstants.MICRO_EDITION_OPTION .startsWith(nextWord)) configuration.microEdition = parseNoArgument(true);
|
||||
@@ -228,9 +257,21 @@ public class ConfigurationParser
|
||||
else if (ConfigurationConstants.PRINT_CONFIGURATION_OPTION .startsWith(nextWord)) configuration.printConfiguration = parseOptionalFile();
|
||||
else if (ConfigurationConstants.DUMP_OPTION .startsWith(nextWord)) configuration.dump = parseOptionalFile();
|
||||
else if (ConfigurationConstants.ADD_CONFIGURATION_DEBUGGING_OPTION .startsWith(nextWord)) configuration.addConfigurationDebugging = parseNoArgument(true);
|
||||
else if (ConfigurationConstants.OPTIMIZE_AGGRESSIVELY .startsWith(nextWord)) configuration.optimizeConservatively = parseNoArgument(false);
|
||||
else if (ConfigurationConstants.ALWAYS_INLINE .startsWith(nextWord)) parseUnsupportedR8Rules(ConfigurationConstants.ALWAYS_INLINE, true);
|
||||
else if (ConfigurationConstants.IDENTIFIER_NAME_STRING .startsWith(nextWord)) parseUnsupportedR8Rules(ConfigurationConstants.IDENTIFIER_NAME_STRING, true);
|
||||
else if (ConfigurationConstants.MAXIMUM_REMOVED_ANDROID_LOG_LEVEL .equals(nextWord)) parseMaximumRemovedAndroidLogLevel();
|
||||
else
|
||||
{
|
||||
throw new ParseException("Unknown option " + reader.locationDescription());
|
||||
if (unknownOptionHandler != null) {
|
||||
unknownOptionHandler.accept(nextWord, reader.lineLocationDescription());
|
||||
while (nextWord != null) {
|
||||
readNextWord();
|
||||
if (nextWord != null && nextWord.startsWith("-")) {
|
||||
continue parseWord;
|
||||
}
|
||||
}
|
||||
} else throw new ParseException("Unknown option " + reader.locationDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,6 +281,7 @@ public class ConfigurationParser
|
||||
* Closes the configuration.
|
||||
* @throws IOException if an IO error occurs while closing the configuration.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
if (reader != null)
|
||||
@@ -249,6 +291,14 @@ public class ConfigurationParser
|
||||
}
|
||||
|
||||
|
||||
private boolean parseKeepKotlinMetadata() throws IOException
|
||||
{
|
||||
System.err.println("The `-keepkotlinmetadata` option is deprecated and will be removed in a future ProGuard release." +
|
||||
"Please use `-keep class kotlin.Metadata` instead.");
|
||||
return parseNoArgument(true);
|
||||
}
|
||||
|
||||
|
||||
private long parseIncludeArgument(long lastModified) throws ParseException, IOException
|
||||
{
|
||||
// Read the configuration file name.
|
||||
@@ -839,7 +889,7 @@ public class ConfigurationParser
|
||||
int requiredUnsetClassAccessFlags = 0;
|
||||
|
||||
// Parse the class annotations and access modifiers until the class keyword.
|
||||
while (!ConfigurationConstants.CLASS_KEYWORD.equals(nextWord))
|
||||
while (!ConfigurationConstants.CLASS_KEYWORD.equals(nextWord) && !configurationEnd(true))
|
||||
{
|
||||
// Strip the negating sign, if any.
|
||||
boolean negated =
|
||||
@@ -1093,12 +1143,25 @@ public class ConfigurationParser
|
||||
// Parse the class member type and name part.
|
||||
|
||||
// Did we get a special wildcard?
|
||||
if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord) ||
|
||||
ConfigurationConstants.ANY_FIELD_KEYWORD .equals(nextWord) ||
|
||||
ConfigurationConstants.ANY_METHOD_KEYWORD .equals(nextWord))
|
||||
boolean isStar = ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord);
|
||||
boolean isFields = ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord);
|
||||
boolean isMethods = ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord);
|
||||
boolean isFieldsOrMethods = isFields || isMethods;
|
||||
|
||||
String type = nextWord;
|
||||
String typeLocation = reader.locationDescription();
|
||||
|
||||
// Try to read the class member name; we need to do this now so that we can check the nextWord
|
||||
// to see if we're parsing a wildcard type.
|
||||
readNextWord("class member name", false, false, false);
|
||||
|
||||
// Is it a wildcard star (short for all members) or is a type wildcard?
|
||||
boolean isReallyStar = isStar && ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord);
|
||||
|
||||
if (isFieldsOrMethods || isReallyStar)
|
||||
{
|
||||
// Act according to the type of wildcard.
|
||||
if (ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD.equals(nextWord))
|
||||
if (isStar)
|
||||
{
|
||||
checkFieldAccessFlags(requiredSetMemberAccessFlags,
|
||||
requiredUnsetMemberAccessFlags);
|
||||
@@ -1118,10 +1181,10 @@ public class ConfigurationParser
|
||||
null,
|
||||
null));
|
||||
}
|
||||
else if (ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord))
|
||||
else if (isFields)
|
||||
{
|
||||
checkFieldAccessFlags(requiredSetMemberAccessFlags,
|
||||
requiredUnsetMemberAccessFlags);
|
||||
requiredUnsetMemberAccessFlags);
|
||||
|
||||
classSpecification.addField(
|
||||
new MemberSpecification(requiredSetMemberAccessFlags,
|
||||
@@ -1130,7 +1193,7 @@ public class ConfigurationParser
|
||||
null,
|
||||
null));
|
||||
}
|
||||
else if (ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord))
|
||||
else if (isMethods)
|
||||
{
|
||||
checkMethodAccessFlags(requiredSetMemberAccessFlags,
|
||||
requiredUnsetMemberAccessFlags);
|
||||
@@ -1143,9 +1206,6 @@ public class ConfigurationParser
|
||||
null));
|
||||
}
|
||||
|
||||
// We still have to read the closing separator.
|
||||
readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'");
|
||||
|
||||
if (!ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord))
|
||||
{
|
||||
throw new ParseException("Expecting separator '" + ConfigurationConstants.SEPARATOR_KEYWORD +
|
||||
@@ -1154,37 +1214,37 @@ public class ConfigurationParser
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure we have a proper type.
|
||||
checkJavaIdentifier("java type");
|
||||
String type = nextWord;
|
||||
String typeLocation = reader.locationDescription();
|
||||
|
||||
readNextWord("class member name");
|
||||
String name = nextWord;
|
||||
checkJavaIdentifier("java type", type, true);
|
||||
|
||||
// Did we get just one word before the opening parenthesis?
|
||||
if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(name))
|
||||
{
|
||||
// This must be a constructor then.
|
||||
// Make sure the type is a proper constructor name.
|
||||
if (!(type.equals(ClassConstants.METHOD_NAME_INIT) ||
|
||||
type.equals(externalClassName) ||
|
||||
type.equals(ClassUtil.externalShortClassName(externalClassName))))
|
||||
// This must be an initializer then.
|
||||
// Make sure the type is a proper initializer name.
|
||||
if (ClassUtil.isInitializer(type))
|
||||
{
|
||||
name = type; // This is either `<init>` or `<clinit>`.
|
||||
type = JavaTypeConstants.VOID;
|
||||
}
|
||||
else if (type.equals(externalClassName) ||
|
||||
type.equals(ClassUtil.externalShortClassName(externalClassName)))
|
||||
{
|
||||
name = ClassConstants.METHOD_NAME_INIT;
|
||||
type = JavaTypeConstants.VOID;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ParseException("Expecting type and name " +
|
||||
"instead of just '" + type +
|
||||
"' before " + reader.locationDescription());
|
||||
}
|
||||
|
||||
// Assign the fixed constructor type and name.
|
||||
type = JavaTypeConstants.VOID;
|
||||
name = ClassConstants.METHOD_NAME_INIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's not a constructor.
|
||||
// It's not an initializer.
|
||||
// Make sure we have a proper name.
|
||||
checkJavaIdentifier("class member name");
|
||||
checkNextWordIsJavaIdentifier("class member name");
|
||||
|
||||
// Read the opening parenthesis or the separating
|
||||
// semi-colon.
|
||||
@@ -1193,7 +1253,7 @@ public class ConfigurationParser
|
||||
}
|
||||
|
||||
// Check if the type actually contains the use of generics.
|
||||
// Can not do it right away as we also support "<init>" as a type (see case above).
|
||||
// Can not do it right away as we also support "<init>" and "<clinit>" as a type (see case above).
|
||||
if (containsGenerics(type))
|
||||
{
|
||||
throw new ParseException("Generics are not allowed (erased) for java type" + typeLocation);
|
||||
@@ -1274,6 +1334,14 @@ public class ConfigurationParser
|
||||
"' before " + reader.locationDescription());
|
||||
}
|
||||
|
||||
// Class initializers are not supposed to have any parameters.
|
||||
if (ClassConstants.METHOD_NAME_CLINIT.equals(name) &&
|
||||
ClassUtil.internalMethodParameterCount(descriptor) > 0)
|
||||
{
|
||||
throw new ParseException("Not expecting method parameters with initializer '" + ClassConstants.METHOD_NAME_CLINIT +
|
||||
"' before " + reader.locationDescription());
|
||||
}
|
||||
|
||||
// Read the separator after the closing parenthesis.
|
||||
readNextWord("separator '" + ConfigurationConstants.SEPARATOR_KEYWORD + "'");
|
||||
|
||||
@@ -1602,7 +1670,7 @@ public class ConfigurationParser
|
||||
{
|
||||
if (checkJavaIdentifiers)
|
||||
{
|
||||
checkJavaIdentifier("java type", allowGenerics);
|
||||
checkNextWordIsJavaIdentifier("java type", allowGenerics);
|
||||
}
|
||||
|
||||
if (replaceSystemProperties)
|
||||
@@ -1869,33 +1937,41 @@ public class ConfigurationParser
|
||||
* Checks whether the given word is a valid Java identifier and throws
|
||||
* a ParseException if it isn't. Wildcard characters are accepted.
|
||||
*/
|
||||
private void checkJavaIdentifier(String expectedDescription)
|
||||
private void checkNextWordIsJavaIdentifier(String expectedDescription)
|
||||
throws ParseException
|
||||
{
|
||||
checkJavaIdentifier(expectedDescription, true);
|
||||
checkNextWordIsJavaIdentifier(expectedDescription, true);
|
||||
}
|
||||
|
||||
private void checkNextWordIsJavaIdentifier(String expectedDescription, boolean allowGenerics) throws ParseException
|
||||
{
|
||||
checkJavaIdentifier(expectedDescription, nextWord, allowGenerics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given word is a valid Java identifier and throws
|
||||
* a ParseException if it isn't. Wildcard characters are accepted.
|
||||
*/
|
||||
private void checkJavaIdentifier(String expectedDescription, boolean allowGenerics)
|
||||
throws ParseException
|
||||
private void checkJavaIdentifier(String expectedDescription, String identifier, boolean allowGenerics)
|
||||
throws ParseException
|
||||
{
|
||||
if (!isJavaIdentifier(nextWord))
|
||||
if (!isValidIdentifier(identifier))
|
||||
{
|
||||
throw new ParseException("Expecting " + expectedDescription +
|
||||
" before " + reader.locationDescription());
|
||||
" before " + reader.locationDescription());
|
||||
}
|
||||
|
||||
if (!allowGenerics && containsGenerics(nextWord))
|
||||
if (!allowGenerics && containsGenerics(identifier))
|
||||
{
|
||||
throw new ParseException("Generics are not allowed (erased) in " + expectedDescription +
|
||||
" " + reader.locationDescription());
|
||||
" " + reader.locationDescription());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidIdentifier(String word)
|
||||
{
|
||||
return useDalvikVerification ? isDexIdentifier(word) : isJavaIdentifier(word);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given word is a valid Java identifier.
|
||||
@@ -1930,6 +2006,43 @@ public class ConfigurationParser
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given word is a valid DEX identifier. Special wildcard characters for
|
||||
* ProGuard class specifiction syntaxs are accepted. The list of valid identifier can be
|
||||
* found at https://source.android.com/docs/core/runtime/dex-format#simplename
|
||||
*/
|
||||
private boolean isDexIdentifier(String word) {
|
||||
if (word.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int[] codePoints = word.codePoints().toArray();
|
||||
|
||||
for (int index = 0; index < codePoints.length; index++) {
|
||||
int c = codePoints[index];
|
||||
|
||||
boolean isLetterOrNumber = Character.isLetterOrDigit(c);
|
||||
boolean isValidSymbol = c == '$' || c == '-' || c == '_';
|
||||
boolean isWithinSupportedUnicodeRanges =
|
||||
(c >= 0x00a1 && c <= 0x1fff)
|
||||
|| (c >= 0x2010 && c <= 0x2027)
|
||||
|| (c >= 0x2030 && c <= 0xd7ff)
|
||||
|| (c >= 0xe000 && c <= 0xffef)
|
||||
|| (c >= 0x10000 && c <= 0x10ffff);
|
||||
boolean isProGuardSymbols =
|
||||
c == '.' || c == '[' || c == ']' || c == '<' || c == '>' || c == '-' || c == '!'
|
||||
|| c == '*' || c == '?' || c == '%';
|
||||
|
||||
if (!(isLetterOrNumber
|
||||
|| isValidSymbol
|
||||
|| isWithinSupportedUnicodeRanges
|
||||
|| isProGuardSymbols)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given word contains angle brackets around
|
||||
@@ -2007,6 +2120,34 @@ public class ConfigurationParser
|
||||
}
|
||||
|
||||
|
||||
private void parseUnsupportedR8Rules(String option, boolean parseClassSpecification) throws IOException, ParseException
|
||||
{
|
||||
readNextWord();
|
||||
|
||||
if (parseClassSpecification)
|
||||
{
|
||||
parseClassSpecificationArguments();
|
||||
}
|
||||
|
||||
|
||||
warnUnsupportedR8Option(option);
|
||||
}
|
||||
|
||||
private void parseMaximumRemovedAndroidLogLevel() throws IOException, ParseException {
|
||||
parseIntegerArgument();
|
||||
if (!configurationEnd(true)) {
|
||||
parseClassSpecificationArguments();
|
||||
}
|
||||
|
||||
warnUnsupportedR8Option(ConfigurationConstants.MAXIMUM_REMOVED_ANDROID_LOG_LEVEL);
|
||||
}
|
||||
|
||||
private static void warnUnsupportedR8Option(String option) {
|
||||
System.out.println("Warning: The R8 option " + option + " is currently not supported by ProGuard.\n" +
|
||||
"This option will have no effect on the optimized artifact.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A main method for testing configuration parsing.
|
||||
*/
|
||||
@@ -2014,10 +2155,7 @@ public class ConfigurationParser
|
||||
{
|
||||
try
|
||||
{
|
||||
ConfigurationParser parser =
|
||||
new ConfigurationParser(args, System.getProperties());
|
||||
|
||||
try
|
||||
try (ConfigurationParser parser = new ConfigurationParser(args, System.getProperties()))
|
||||
{
|
||||
parser.parse(new Configuration());
|
||||
}
|
||||
@@ -2025,10 +2163,6 @@ public class ConfigurationParser
|
||||
{
|
||||
ex.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
parser.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
@@ -20,6 +20,9 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.util.WarningLogger;
|
||||
import proguard.classfile.util.WarningPrinter;
|
||||
|
||||
import java.io.*;
|
||||
@@ -29,15 +32,17 @@ import java.io.*;
|
||||
*
|
||||
* @author Eric Lafortune
|
||||
*/
|
||||
public class ConfigurationChecker
|
||||
public class ConfigurationVerifier
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(ConfigurationVerifier.class);
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new ConfigurationChecker with the given configuration.
|
||||
* Creates a new ConfigurationVerifier with the given configuration.
|
||||
*/
|
||||
public ConfigurationChecker(Configuration configuration)
|
||||
public ConfigurationVerifier(Configuration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
@@ -48,10 +53,6 @@ public class ConfigurationChecker
|
||||
*/
|
||||
public void check() throws IOException
|
||||
{
|
||||
// We're using the system's default character encoding for writing to
|
||||
// the standard output.
|
||||
PrintWriter out = new PrintWriter(System.out, true);
|
||||
|
||||
ClassPath programJars = configuration.programJars;
|
||||
ClassPath libraryJars = configuration.libraryJars;
|
||||
|
||||
@@ -61,14 +62,39 @@ public class ConfigurationChecker
|
||||
throw new IOException("The input is empty. You have to specify one or more '-injars' options.");
|
||||
}
|
||||
|
||||
checkInputJarFirst(programJars);
|
||||
|
||||
checkOutputJarFilter(programJars);
|
||||
|
||||
// Check for conflicts between input/output entries of the class paths.
|
||||
checkConflicts(programJars, programJars);
|
||||
checkConflicts(programJars, libraryJars);
|
||||
checkConflicts(libraryJars, libraryJars);
|
||||
|
||||
printNotes(configuration, programJars, logger);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks that the input application is specified before any input libraries.
|
||||
*/
|
||||
private void checkInputJarFirst(ClassPath programJars) throws IOException
|
||||
{
|
||||
// Check that the first jar is an input jar.
|
||||
ClassPathEntry firstEntry = programJars.get(0);
|
||||
if (firstEntry.isOutput())
|
||||
{
|
||||
throw new IOException("The output jar [" + firstEntry.getName() +
|
||||
"] must be specified after an input jar, or it will be empty.");
|
||||
"] must be specified after an input jar, or it will be empty.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks that the first of two subsequent output jars has a filter.
|
||||
*/
|
||||
private void checkOutputJarFilter(ClassPath programJars) throws IOException
|
||||
{
|
||||
// Check that the first of two subsequent the output jars has a filter.
|
||||
for (int index = 0; index < programJars.size() - 1; index++)
|
||||
{
|
||||
@@ -81,91 +107,6 @@ public class ConfigurationChecker
|
||||
"] must have a filter, or all subsequent output jars will be empty.");
|
||||
}
|
||||
}
|
||||
|
||||
// Check for conflicts between input/output entries of the class paths.
|
||||
checkConflicts(programJars, programJars);
|
||||
checkConflicts(programJars, libraryJars);
|
||||
checkConflicts(libraryJars, libraryJars);
|
||||
|
||||
// Print out some general notes if necessary.
|
||||
if ((configuration.note == null ||
|
||||
!configuration.note.isEmpty()))
|
||||
{
|
||||
// Check for potential problems with mixed-case class names on
|
||||
// case-insensitive file systems.
|
||||
if (configuration.obfuscate &&
|
||||
configuration.useMixedCaseClassNames &&
|
||||
configuration.classObfuscationDictionary == null)
|
||||
{
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.startsWith("windows") ||
|
||||
os.startsWith("mac os"))
|
||||
{
|
||||
// Go over all program class path entries.
|
||||
for (int index = 0; index < programJars.size(); index++)
|
||||
{
|
||||
// Is it an output directory?
|
||||
ClassPathEntry entry = programJars.get(index);
|
||||
if (entry.isOutput() &&
|
||||
!entry.isApk() &&
|
||||
!entry.isJar() &&
|
||||
!entry.isAar() &&
|
||||
!entry.isWar() &&
|
||||
!entry.isEar() &&
|
||||
!entry.isJmod() &&
|
||||
!entry.isZip())
|
||||
{
|
||||
out.println("Note: you're writing the processed class files to a directory [" + entry.getName() +"].");
|
||||
out.println(" This will likely cause problems with obfuscated mixed-case class names.");
|
||||
out.println(" You should consider writing the output to a jar file, or otherwise");
|
||||
out.println(" specify '-dontusemixedcaseclassnames'.");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if -adaptresourcefilecontents has a proper filter.
|
||||
if (configuration.adaptResourceFileContents != null &&
|
||||
(configuration.adaptResourceFileContents.isEmpty() ||
|
||||
configuration.adaptResourceFileContents.get(0).equals(ConfigurationConstants.ANY_FILE_KEYWORD)))
|
||||
{
|
||||
out.println("Note: you're specifying '-adaptresourcefilecontents' for all resource files.");
|
||||
out.println(" This will most likely cause problems with binary files.");
|
||||
}
|
||||
|
||||
// Check if all -keepclassmembers options indeed have class members.
|
||||
WarningPrinter keepClassMemberNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
|
||||
new KeepClassMemberChecker(keepClassMemberNotePrinter).checkClassSpecifications(configuration.keep);
|
||||
|
||||
// Check if -assumenosideffects options don't specify all methods.
|
||||
WarningPrinter assumeNoSideEffectsNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
|
||||
new AssumeNoSideEffectsChecker(assumeNoSideEffectsNotePrinter).checkClassSpecifications(configuration.assumeNoSideEffects);
|
||||
|
||||
// Print out a summary of the notes, if necessary.
|
||||
int keepClassMemberNoteCount = keepClassMemberNotePrinter.getWarningCount();
|
||||
if (keepClassMemberNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + keepClassMemberNoteCount +
|
||||
" '-keepclassmembers' options that didn't specify class");
|
||||
out.println(" members. You should specify at least some class members or consider");
|
||||
out.println(" if you just need '-keep'.");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#classmembers)");
|
||||
}
|
||||
|
||||
int assumeNoSideEffectsNoteCount = assumeNoSideEffectsNotePrinter.getWarningCount();
|
||||
if (assumeNoSideEffectsNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + assumeNoSideEffectsNoteCount +
|
||||
" '-assumenosideeffects' options that try to match all");
|
||||
out.println(" methods with wildcards. This will likely cause problems with methods like");
|
||||
out.println(" 'wait()' and 'notify()'. You should specify the methods more precisely.");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#nosideeffects)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -226,4 +167,86 @@ public class ConfigurationChecker
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void printNotes(Configuration configuration, ClassPath programJars, Logger logger) throws IOException
|
||||
{
|
||||
// Print out some general notes if necessary.
|
||||
if ((configuration.note == null ||
|
||||
!configuration.note.isEmpty()))
|
||||
{
|
||||
// Check for potential problems with mixed-case class names on
|
||||
// case-insensitive file systems.
|
||||
if (configuration.obfuscate &&
|
||||
configuration.useMixedCaseClassNames &&
|
||||
configuration.classObfuscationDictionary == null)
|
||||
{
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.startsWith("windows") ||
|
||||
os.startsWith("mac os"))
|
||||
{
|
||||
// Go over all program class path entries.
|
||||
for (int index = 0; index < programJars.size(); index++)
|
||||
{
|
||||
// Is it an output directory?
|
||||
ClassPathEntry entry = programJars.get(index);
|
||||
if (entry.isOutput() &&
|
||||
!entry.isApk() &&
|
||||
!entry.isJar() &&
|
||||
!entry.isAar() &&
|
||||
!entry.isWar() &&
|
||||
!entry.isEar() &&
|
||||
!entry.isJmod() &&
|
||||
!entry.isZip())
|
||||
{
|
||||
logger.info("Note: you're writing the processed class files to a directory [{}].", entry.getName());
|
||||
logger.info(" This will likely cause problems with obfuscated mixed-case class names.");
|
||||
logger.info(" You should consider writing the output to a jar file, or otherwise");
|
||||
logger.info(" specify '-dontusemixedcaseclassnames'.");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if -adaptresourcefilecontents has a proper filter.
|
||||
if (configuration.adaptResourceFileContents != null &&
|
||||
(configuration.adaptResourceFileContents.isEmpty() ||
|
||||
configuration.adaptResourceFileContents.get(0).equals(ConfigurationConstants.ANY_FILE_KEYWORD)))
|
||||
{
|
||||
logger.info("Note: you're specifying '-adaptresourcefilecontents' for all resource files.");
|
||||
logger.info(" This will most likely cause problems with binary files.");
|
||||
}
|
||||
|
||||
// Check if all -keepclassmembers options indeed have class members.
|
||||
WarningPrinter keepClassMemberNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
new KeepClassMemberChecker(keepClassMemberNotePrinter).checkClassSpecifications(configuration.keep);
|
||||
|
||||
// Check if -assumenosideffects options don't specify all methods.
|
||||
WarningPrinter assumeNoSideEffectsNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
new AssumeNoSideEffectsChecker(assumeNoSideEffectsNotePrinter).checkClassSpecifications(configuration.assumeNoSideEffects);
|
||||
|
||||
// Print out a summary of the notes, if necessary.
|
||||
int keepClassMemberNoteCount = keepClassMemberNotePrinter.getWarningCount();
|
||||
if (keepClassMemberNoteCount > 0)
|
||||
{
|
||||
logger.info("Note: there were {} '-keepclassmembers' options that didn't specify class", keepClassMemberNoteCount);
|
||||
logger.info(" members. You should specify at least some class members or consider");
|
||||
logger.info(" if you just need '-keep'.");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#classmembers)");
|
||||
}
|
||||
|
||||
int assumeNoSideEffectsNoteCount = assumeNoSideEffectsNotePrinter.getWarningCount();
|
||||
if (assumeNoSideEffectsNoteCount > 0)
|
||||
{
|
||||
logger.info("Note: there were {} '-assumenosideeffects' options that try to match all", assumeNoSideEffectsNoteCount);
|
||||
logger.info(" methods with wildcards. This will likely cause problems with methods like");
|
||||
logger.info(" 'wait()' and 'notify()'. You should specify the methods more precisely.");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#nosideeffects)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,11 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.optimize.Optimizer;
|
||||
import proguard.util.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -33,8 +36,10 @@ import java.util.*;
|
||||
*
|
||||
* @author Eric Lafortune
|
||||
*/
|
||||
public class ConfigurationWriter
|
||||
public class ConfigurationWriter implements AutoCloseable
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(ConfigurationWriter.class);
|
||||
|
||||
private static final String[] KEEP_OPTIONS = new String[]
|
||||
{
|
||||
ConfigurationConstants.KEEP_OPTION,
|
||||
@@ -45,7 +50,8 @@ public class ConfigurationWriter
|
||||
|
||||
|
||||
private final PrintWriter writer;
|
||||
private File baseDir;
|
||||
private File configurationFile;
|
||||
private String baseDirName;
|
||||
|
||||
|
||||
/**
|
||||
@@ -55,7 +61,11 @@ public class ConfigurationWriter
|
||||
{
|
||||
this(PrintWriterUtil.createPrintWriterOut(configurationFile));
|
||||
|
||||
baseDir = configurationFile.getParentFile();
|
||||
this.configurationFile = configurationFile;
|
||||
if (configurationFile.getParentFile() != null)
|
||||
{
|
||||
baseDirName = configurationFile.getParentFile().getAbsolutePath() + File.separator;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,9 +81,10 @@ public class ConfigurationWriter
|
||||
/**
|
||||
* Closes this ConfigurationWriter.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
writer.close();
|
||||
PrintWriterUtil.closePrintWriter(configurationFile, writer);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +95,11 @@ public class ConfigurationWriter
|
||||
*/
|
||||
public void write(Configuration configuration) throws IOException
|
||||
{
|
||||
if (configuration.printConfiguration != null)
|
||||
{
|
||||
logger.info("Printing configuration to [{}]...", PrintWriterUtil.fileName(configuration.printConfiguration));
|
||||
}
|
||||
|
||||
// Write the program class path (input and output entries).
|
||||
writeJarOptions(ConfigurationConstants.INJARS_OPTION,
|
||||
ConfigurationConstants.OUTJARS_OPTION,
|
||||
@@ -133,6 +149,7 @@ public class ConfigurationWriter
|
||||
writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION, configuration.adaptResourceFileNames);
|
||||
writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION, configuration.adaptResourceFileContents);
|
||||
writeOption(ConfigurationConstants.KEEP_KOTLIN_METADATA, configuration.keepKotlinMetadata);
|
||||
writeOption(ConfigurationConstants.DONT_PROCESS_KOTLIN_METADATA, configuration.dontProcessKotlinMetadata);
|
||||
|
||||
writeOption(ConfigurationConstants.DONT_PREVERIFY_OPTION, !configuration.preverify);
|
||||
writeOption(ConfigurationConstants.MICRO_EDITION_OPTION, configuration.microEdition);
|
||||
@@ -151,7 +168,8 @@ public class ConfigurationWriter
|
||||
writeOption(ConfigurationConstants.DUMP_OPTION, configuration.dump);
|
||||
writeOption(ConfigurationConstants.ADD_CONFIGURATION_DEBUGGING_OPTION, configuration.addConfigurationDebugging);
|
||||
|
||||
writeOption(ConfigurationConstants.PRINT_SEEDS_OPTION, configuration.printSeeds);
|
||||
writeOption(ConfigurationConstants.PRINT_SEEDS_OPTION, configuration.printSeeds);
|
||||
writeOption(ConfigurationConstants.OPTIMIZE_AGGRESSIVELY, !configuration.optimizeConservatively);
|
||||
writer.println();
|
||||
|
||||
// Write the "why are you keeping" options.
|
||||
@@ -786,13 +804,9 @@ public class ConfigurationWriter
|
||||
String fileName = file.getAbsolutePath();
|
||||
|
||||
// See if we can convert the file name into a relative file name.
|
||||
if (baseDir != null)
|
||||
if (baseDirName != null && fileName.startsWith(baseDirName))
|
||||
{
|
||||
String baseDirName = baseDir.getAbsolutePath() + File.separator;
|
||||
if (fileName.startsWith(baseDirName))
|
||||
{
|
||||
fileName = fileName.substring(baseDirName.length());
|
||||
}
|
||||
fileName = fileName.substring(baseDirName.length());
|
||||
}
|
||||
|
||||
return quotedString(fileName);
|
||||
@@ -806,6 +820,7 @@ public class ConfigurationWriter
|
||||
{
|
||||
return string.length() == 0 ||
|
||||
string.indexOf(' ') >= 0 ||
|
||||
string.indexOf('#') >= 0 ||
|
||||
string.indexOf('@') >= 0 ||
|
||||
string.indexOf('{') >= 0 ||
|
||||
string.indexOf('}') >= 0 ||
|
||||
@@ -814,7 +829,7 @@ public class ConfigurationWriter
|
||||
string.indexOf(':') >= 0 ||
|
||||
string.indexOf(';') >= 0 ||
|
||||
string.indexOf(',') >= 0 ? ("'" + string + "'") :
|
||||
( string );
|
||||
( string );
|
||||
}
|
||||
|
||||
|
||||
@@ -823,10 +838,8 @@ public class ConfigurationWriter
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
try
|
||||
try (ConfigurationWriter writer = new ConfigurationWriter(new File(args[0])))
|
||||
{
|
||||
ConfigurationWriter writer = new ConfigurationWriter(new File(args[0]));
|
||||
|
||||
writer.write(new Configuration());
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -2,7 +2,7 @@
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
* Copyright (c) 2002-2022 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -20,12 +20,28 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import proguard.classfile.ClassConstants;
|
||||
import proguard.io.*;
|
||||
import proguard.util.*;
|
||||
import proguard.io.DataEntryNameFilter;
|
||||
import proguard.io.DataEntryReader;
|
||||
import proguard.io.FilteredDataEntryReader;
|
||||
import proguard.io.JarReader;
|
||||
import proguard.io.PrefixStrippingDataEntryReader;
|
||||
import proguard.io.RenamedDataEntryReader;
|
||||
import proguard.util.AndMatcher;
|
||||
import proguard.util.ExtensionMatcher;
|
||||
import proguard.util.FileNameParser;
|
||||
import proguard.util.ListFunctionParser;
|
||||
import proguard.util.ListParser;
|
||||
import proguard.util.NotMatcher;
|
||||
import proguard.util.SingleFunctionParser;
|
||||
import proguard.util.StringFunction;
|
||||
import proguard.util.StringMatcher;
|
||||
import proguard.util.WildcardManager;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static proguard.classfile.ClassConstants.CLASS_FILE_EXTENSION;
|
||||
|
||||
/**
|
||||
* This class can create DataEntryReader instances based on class path entries.
|
||||
@@ -36,10 +52,11 @@ import java.util.*;
|
||||
*/
|
||||
public class DataEntryReaderFactory
|
||||
{
|
||||
|
||||
private static final String VERSIONS_PATTERN = "META-INF/versions";
|
||||
private static final String VERSIONS_EXCLUDE = "!META-INF/versions/**";
|
||||
|
||||
private static final String CLASS_FILE_PREFIX = "classes/";
|
||||
private static final String JMOD_CLASS_FILE_PREFIX = "classes/";
|
||||
|
||||
|
||||
private final boolean android;
|
||||
@@ -57,7 +74,6 @@ public class DataEntryReaderFactory
|
||||
this.android = android;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a DataEntryReader that can read the given class path entry.
|
||||
*
|
||||
@@ -66,43 +82,7 @@ public class DataEntryReaderFactory
|
||||
* classes and resource files can be delegated.
|
||||
* @return a DataEntryReader for reading the given class path entry.
|
||||
*/
|
||||
public DataEntryReader createDataEntryReader(ClassPathEntry classPathEntry,
|
||||
DataEntryReader reader)
|
||||
{
|
||||
return createDataEntryReader("", classPathEntry, reader, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DataEntryReader that can read the given class path entry.
|
||||
*
|
||||
* @param messagePrefix a prefix for messages that are printed out (with System.out)
|
||||
* @param classPathEntry the input class path entry.
|
||||
* @param reader a data entry reader to which the reading of actual
|
||||
* classes and resource files can be delegated.
|
||||
* @return a DataEntryReader for reading the given class path entry.
|
||||
*/
|
||||
public DataEntryReader createDataEntryReader(String messagePrefix,
|
||||
ClassPathEntry classPathEntry,
|
||||
DataEntryReader reader)
|
||||
{
|
||||
return createDataEntryReader(messagePrefix, classPathEntry, reader, System.out);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a DataEntryReader that can read the given class path entry.
|
||||
*
|
||||
* @param messagePrefix a prefix for messages that are printed out.
|
||||
* @param classPathEntry the input class path entry.
|
||||
* @param reader a data entry reader to which the reading of actual
|
||||
* classes and resource files can be delegated.
|
||||
* @param out an optional print stream for messages.
|
||||
* @return a DataEntryReader for reading the given class path entry.
|
||||
*/
|
||||
public DataEntryReader createDataEntryReader(String messagePrefix,
|
||||
ClassPathEntry classPathEntry,
|
||||
DataEntryReader reader,
|
||||
PrintStream out)
|
||||
public DataEntryReader createDataEntryReader(ClassPathEntry classPathEntry, DataEntryReader reader)
|
||||
{
|
||||
boolean isApk = classPathEntry.isApk();
|
||||
boolean isAab = classPathEntry.isAab();
|
||||
@@ -113,39 +93,15 @@ public class DataEntryReaderFactory
|
||||
boolean isJmod = classPathEntry.isJmod();
|
||||
boolean isZip = classPathEntry.isZip();
|
||||
|
||||
List filter = getFilterExcludingVersionedClasses(classPathEntry);
|
||||
List apkFilter = classPathEntry.getApkFilter();
|
||||
List aabFilter = classPathEntry.getAabFilter();
|
||||
List jarFilter = classPathEntry.getJarFilter();
|
||||
List aarFilter = classPathEntry.getAarFilter();
|
||||
List warFilter = classPathEntry.getWarFilter();
|
||||
List earFilter = classPathEntry.getEarFilter();
|
||||
List jmodFilter = classPathEntry.getJmodFilter();
|
||||
List zipFilter = classPathEntry.getZipFilter();
|
||||
|
||||
if (out != null)
|
||||
{
|
||||
out.println(messagePrefix +
|
||||
(isApk ? "apk" :
|
||||
isAab ? "aab" :
|
||||
isJar ? "jar" :
|
||||
isAar ? "aar" :
|
||||
isWar ? "war" :
|
||||
isEar ? "ear" :
|
||||
isJmod ? "jmod" :
|
||||
isZip ? "zip" :
|
||||
"directory") +
|
||||
" [" + classPathEntry.getName() + "]" +
|
||||
(filter != null ||
|
||||
apkFilter != null ||
|
||||
aabFilter != null ||
|
||||
jarFilter != null ||
|
||||
aarFilter != null ||
|
||||
warFilter != null ||
|
||||
earFilter != null ||
|
||||
jmodFilter != null ||
|
||||
zipFilter != null ? " (filtered)" : ""));
|
||||
}
|
||||
List<String> filter = getFilterExcludingVersionedClasses(classPathEntry);
|
||||
List<String> apkFilter = classPathEntry.getApkFilter();
|
||||
List<String> aabFilter = classPathEntry.getAabFilter();
|
||||
List<String> jarFilter = classPathEntry.getJarFilter();
|
||||
List<String> aarFilter = classPathEntry.getAarFilter();
|
||||
List<String> warFilter = classPathEntry.getWarFilter();
|
||||
List<String> earFilter = classPathEntry.getEarFilter();
|
||||
List<String> jmodFilter = classPathEntry.getJmodFilter();
|
||||
List<String> zipFilter = classPathEntry.getZipFilter();
|
||||
|
||||
// Add a renaming filter, if specified.
|
||||
if (filter != null)
|
||||
@@ -224,14 +180,14 @@ public class DataEntryReaderFactory
|
||||
boolean stripClassesPrefix,
|
||||
boolean stripJmodHeader,
|
||||
boolean isJar,
|
||||
List jarFilter,
|
||||
List<String> jarFilter,
|
||||
String jarExtension)
|
||||
{
|
||||
if (stripClassesPrefix)
|
||||
{
|
||||
reader = new FilteredDataEntryReader(
|
||||
new DataEntryNameFilter(new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION)),
|
||||
new PrefixStrippingDataEntryReader(CLASS_FILE_PREFIX, reader),
|
||||
new DataEntryNameFilter(new ExtensionMatcher(CLASS_FILE_EXTENSION)),
|
||||
new PrefixStrippingDataEntryReader(JMOD_CLASS_FILE_PREFIX, reader),
|
||||
reader);
|
||||
}
|
||||
|
||||
@@ -261,11 +217,15 @@ public class DataEntryReaderFactory
|
||||
if (android)
|
||||
{
|
||||
jarMatcher =
|
||||
new AndMatcher(new NotMatcher(
|
||||
new FixedStringMatcher("assets/",
|
||||
new ConstantMatcher(true))),
|
||||
new AndMatcher(
|
||||
new AndMatcher(
|
||||
new NotMatcher(
|
||||
new ListParser(new FileNameParser()).parse("assets/**,*/assets/**")),
|
||||
|
||||
jarMatcher);
|
||||
new NotMatcher(
|
||||
new ListParser(new FileNameParser()).parse("res/**,*/res/**"))),
|
||||
|
||||
jarMatcher);
|
||||
}
|
||||
|
||||
// Only unzip the right type of jars.
|
||||
@@ -285,29 +245,25 @@ public class DataEntryReaderFactory
|
||||
* If no custom filter targeting a specific version is used, exclude such classes
|
||||
* from being read.
|
||||
*/
|
||||
public static List getFilterExcludingVersionedClasses(ClassPathEntry classPathEntry)
|
||||
public static List<String> getFilterExcludingVersionedClasses(ClassPathEntry classPathEntry)
|
||||
{
|
||||
List originalFilter = classPathEntry.getFilter();
|
||||
List<String> originalFilter = classPathEntry.getFilter();
|
||||
if (originalFilter == null)
|
||||
{
|
||||
return Arrays.asList(VERSIONS_EXCLUDE);
|
||||
return Collections.singletonList(VERSIONS_EXCLUDE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is already a custom filter for versioned classes
|
||||
// assume that the filter is properly setup.
|
||||
ListIterator it = originalFilter.listIterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
String element = (String) it.next();
|
||||
if (element.contains(VERSIONS_PATTERN))
|
||||
{
|
||||
for (String element : originalFilter) {
|
||||
if (element.contains(VERSIONS_PATTERN)) {
|
||||
return originalFilter;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, exclude all versioned classes.
|
||||
List filter = new ArrayList();
|
||||
List<String> filter = new ArrayList<>();
|
||||
filter.add(VERSIONS_EXCLUDE);
|
||||
filter.addAll(originalFilter);
|
||||
return filter;
|
||||
@@ -2,7 +2,7 @@
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
* Copyright (c) 2002-2022 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -28,6 +28,9 @@ import proguard.util.*;
|
||||
import java.io.File;
|
||||
import java.security.KeyStore;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static proguard.classfile.ClassConstants.CLASS_FILE_EXTENSION;
|
||||
|
||||
/**
|
||||
* This class can create DataEntryWriter instances based on class paths. The
|
||||
@@ -38,6 +41,9 @@ import java.util.*;
|
||||
*/
|
||||
public class DataEntryWriterFactory
|
||||
{
|
||||
|
||||
private static final boolean ENABLE_ZIP64_SUPPORT = System.getProperty("enable.zip64.support") != null;
|
||||
|
||||
private static final String CLASS_FILE_PATTERN = "**.class";
|
||||
private static final String CLASS_FILE_PREFIX = "classes/";
|
||||
|
||||
@@ -62,10 +68,12 @@ public class DataEntryWriterFactory
|
||||
private final StringMatcher uncompressedFilter;
|
||||
private final int uncompressedAlignment;
|
||||
private final boolean pageAlignNativeLibs;
|
||||
private final boolean mergeBundleJars;
|
||||
private final boolean mergeAarJars;
|
||||
private final KeyStore.PrivateKeyEntry[] privateKeyEntries;
|
||||
|
||||
private Map<File,DataEntryWriter> jarWriterCache = new HashMap();
|
||||
private final Map<File,DataEntryWriter> jarWriterCache = new HashMap<>();
|
||||
|
||||
private final Function<DataEntryWriter, DataEntryWriter> alternativeClassDataEntryWriterProvider;
|
||||
|
||||
|
||||
/**
|
||||
@@ -82,8 +90,8 @@ public class DataEntryWriterFactory
|
||||
* of uncompressed entries.
|
||||
* @param pageAlignNativeLibs specifies whether to align native
|
||||
* libraries at page boundaries.
|
||||
* @param mergeBundleJars specifies whether to merge all jars
|
||||
* in an Android app bundle into a
|
||||
* @param mergeAarJars specifies whether to merge all jars
|
||||
* in an Android library aar into a
|
||||
* single jar.
|
||||
* @param privateKeyEntries optional private keys to sign jars.
|
||||
*/
|
||||
@@ -93,17 +101,62 @@ public class DataEntryWriterFactory
|
||||
StringMatcher uncompressedFilter,
|
||||
int uncompressedAlignment,
|
||||
boolean pageAlignNativeLibs,
|
||||
boolean mergeBundleJars,
|
||||
boolean mergeAarJars,
|
||||
KeyStore.PrivateKeyEntry[] privateKeyEntries)
|
||||
{
|
||||
this.programClassPool = programClassPool;
|
||||
this.resourceFilePool = resourceFilePool;
|
||||
this.modificationTime = modificationTime;
|
||||
this.uncompressedFilter = uncompressedFilter;
|
||||
this.uncompressedAlignment = uncompressedAlignment;
|
||||
this.pageAlignNativeLibs = pageAlignNativeLibs;
|
||||
this.mergeBundleJars = mergeBundleJars;
|
||||
this.privateKeyEntries = privateKeyEntries;
|
||||
this(
|
||||
programClassPool,
|
||||
resourceFilePool,
|
||||
modificationTime,
|
||||
uncompressedFilter,
|
||||
uncompressedAlignment,
|
||||
pageAlignNativeLibs,
|
||||
mergeAarJars,
|
||||
privateKeyEntries,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DataEntryWriterFactory.
|
||||
*
|
||||
* @param programClassPool the program class pool to process.
|
||||
* @param resourceFilePool the resource file pool to process.
|
||||
* @param modificationTime the modification date and time of
|
||||
* the zip entries, in DOS
|
||||
* format.
|
||||
* @param uncompressedFilter an optional filter for files that
|
||||
* should not be compressed.
|
||||
* @param uncompressedAlignment the desired alignment for the data
|
||||
* of uncompressed entries.
|
||||
* @param pageAlignNativeLibs specifies whether to align native
|
||||
* libraries at page boundaries.
|
||||
* @param mergeAarJars specifies whether to merge all jars
|
||||
* in an Android app bundle into a
|
||||
* single jar.
|
||||
* @param privateKeyEntries optional private keys to sign jars.
|
||||
* @param alternativeClassDataEntryWriterProvider optional, to provide an alternative class writer,
|
||||
* instead of the default {@link ClassDataEntryWriter}.
|
||||
*/
|
||||
public DataEntryWriterFactory(ClassPool programClassPool,
|
||||
ResourceFilePool resourceFilePool,
|
||||
int modificationTime,
|
||||
StringMatcher uncompressedFilter,
|
||||
int uncompressedAlignment,
|
||||
boolean pageAlignNativeLibs,
|
||||
boolean mergeAarJars,
|
||||
KeyStore.PrivateKeyEntry[] privateKeyEntries,
|
||||
Function<DataEntryWriter, DataEntryWriter> alternativeClassDataEntryWriterProvider)
|
||||
{
|
||||
this.programClassPool = programClassPool;
|
||||
this.resourceFilePool = resourceFilePool;
|
||||
this.modificationTime = modificationTime;
|
||||
this.uncompressedFilter = uncompressedFilter;
|
||||
this.uncompressedAlignment = uncompressedAlignment;
|
||||
this.pageAlignNativeLibs = pageAlignNativeLibs;
|
||||
this.mergeAarJars = mergeAarJars;
|
||||
this.privateKeyEntries = privateKeyEntries;
|
||||
this.alternativeClassDataEntryWriterProvider = alternativeClassDataEntryWriterProvider;
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +243,7 @@ public class DataEntryWriterFactory
|
||||
{
|
||||
File file = classPathEntry.getFile();
|
||||
|
||||
boolean isDex = classPathEntry.isDex();
|
||||
boolean isApk = classPathEntry.isApk();
|
||||
boolean isAab = classPathEntry.isAab();
|
||||
boolean isJar = classPathEntry.isJar();
|
||||
@@ -199,120 +253,127 @@ public class DataEntryWriterFactory
|
||||
boolean isJmod = classPathEntry.isJmod();
|
||||
boolean isZip = classPathEntry.isZip();
|
||||
|
||||
List filter = DataEntryReaderFactory.getFilterExcludingVersionedClasses(classPathEntry);
|
||||
List apkFilter = classPathEntry.getApkFilter();
|
||||
List aabFilter = classPathEntry.getAabFilter();
|
||||
List jarFilter = classPathEntry.getJarFilter();
|
||||
List aarFilter = classPathEntry.getAarFilter();
|
||||
List warFilter = classPathEntry.getWarFilter();
|
||||
List earFilter = classPathEntry.getEarFilter();
|
||||
List jmodFilter = classPathEntry.getJmodFilter();
|
||||
List zipFilter = classPathEntry.getZipFilter();
|
||||
boolean isFile = isDex || isApk || isAab || isJar || isAar || isWar || isEar || isJmod || isZip;
|
||||
|
||||
System.out.println("Preparing " +
|
||||
(privateKeyEntries == null ? "" : "signed ") +
|
||||
"output " +
|
||||
(isApk ? "apk" :
|
||||
isAab ? "aab" :
|
||||
isJar ? "jar" :
|
||||
isAar ? "aar" :
|
||||
isWar ? "war" :
|
||||
isEar ? "ear" :
|
||||
isJmod ? "jmod" :
|
||||
isZip ? "zip" :
|
||||
"directory") +
|
||||
" [" + classPathEntry.getName() + "]" +
|
||||
(filter != null ||
|
||||
apkFilter != null ||
|
||||
aabFilter != null ||
|
||||
jarFilter != null ||
|
||||
aarFilter != null ||
|
||||
warFilter != null ||
|
||||
earFilter != null ||
|
||||
jmodFilter != null ||
|
||||
zipFilter != null ? " (filtered)" : ""));
|
||||
List<String> filter = DataEntryReaderFactory.getFilterExcludingVersionedClasses(classPathEntry);
|
||||
List<String> apkFilter = classPathEntry.getApkFilter();
|
||||
List<String> aabFilter = classPathEntry.getAabFilter();
|
||||
List<String> jarFilter = classPathEntry.getJarFilter();
|
||||
List<String> aarFilter = classPathEntry.getAarFilter();
|
||||
List<String> warFilter = classPathEntry.getWarFilter();
|
||||
List<String> earFilter = classPathEntry.getEarFilter();
|
||||
List<String> jmodFilter = classPathEntry.getJmodFilter();
|
||||
List<String> zipFilter = classPathEntry.getZipFilter();
|
||||
|
||||
// Create the writer for the main file or directory.
|
||||
DataEntryWriter writer =
|
||||
isApk ||
|
||||
isAab ||
|
||||
isJar ||
|
||||
isAar ||
|
||||
isWar ||
|
||||
isEar ||
|
||||
isJmod ||
|
||||
isZip ?
|
||||
new FixedFileWriter(file) :
|
||||
new DirectoryWriter(file);
|
||||
DataEntryWriter writer = isFile ?
|
||||
new FixedFileWriter(file) :
|
||||
new DirectoryWriter(file);
|
||||
|
||||
// If the output is an archive, we'll flatten (unpack the contents of)
|
||||
// higher level input archives, e.g. when writing into a jar file, we
|
||||
// flatten zip files.
|
||||
boolean flattenApks = false;
|
||||
boolean flattenAabs = flattenApks || isApk;
|
||||
boolean flattenJars = flattenAabs || isAab;
|
||||
boolean flattenAars = flattenJars || isJar;
|
||||
boolean flattenWars = flattenAars || isAar;
|
||||
boolean flattenEars = flattenWars || isWar;
|
||||
boolean flattenJmods = flattenEars || isEar;
|
||||
boolean flattenZips = flattenJmods || isJmod;
|
||||
|
||||
// Set up the filtered jar writers.
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenZips, isZip, false, ".zip", zipFilter, null, false, null);
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenJmods, isJmod, false, ".jmod", jmodFilter, JMOD_HEADER, false, JMOD_PREFIXES);
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenEars, isEar, false, ".ear", earFilter, null, false, null);
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenWars, isWar, false, ".war", warFilter, null, false, WAR_PREFIXES);
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenAars, isAar, false, ".aar", aarFilter, null, false, null);
|
||||
|
||||
if (isAar && mergeBundleJars)
|
||||
if (isDex)
|
||||
{
|
||||
// If we're writing an obfuscated AAR, all input jars need to
|
||||
// be merged into a final classes.jar file.
|
||||
// A dex file can't contain resource files.
|
||||
writer =
|
||||
new FilteredDataEntryWriter(new DataEntryNameFilter(new ExtensionMatcher(".jar")),
|
||||
new RenamedDataEntryWriter(new ConstantStringFunction("classes.jar"), writer),
|
||||
new FilteredDataEntryWriter(
|
||||
new DataEntryNameFilter(
|
||||
new ExtensionMatcher(".dex")),
|
||||
writer);
|
||||
}
|
||||
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenJars, isJar, false, ".jar", jarFilter, null, false, null);
|
||||
|
||||
// Either we create an aab or apk; they can not be nested.
|
||||
writer = isAab ?
|
||||
wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenAabs, isAab, true, ".aab", aabFilter, null, false, null) :
|
||||
wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenApks, isApk, false, ".apk", apkFilter, null, pageAlignNativeLibs, null);
|
||||
|
||||
// Create a writer for plain class files. Don't close the enclosed
|
||||
// writer through it, but let it be closed later on.
|
||||
DataEntryWriter classWriter =
|
||||
new ClassDataEntryWriter(programClassPool,
|
||||
new NonClosingDataEntryWriter(writer));
|
||||
|
||||
// Add a renaming filter, if specified.
|
||||
if (filter != null)
|
||||
else
|
||||
{
|
||||
WildcardManager wildcardManager = new WildcardManager();
|
||||
// If the output is an archive, we'll flatten (unpack the contents of)
|
||||
// higher level input archives, e.g. when writing into a jar file, we
|
||||
// flatten zip files.
|
||||
boolean flattenApks = false;
|
||||
boolean flattenAabs = flattenApks || isApk;
|
||||
boolean flattenJars = flattenAabs || isAab;
|
||||
boolean flattenAars = flattenJars || isJar;
|
||||
boolean flattenWars = flattenAars || isAar;
|
||||
boolean flattenEars = flattenWars || isWar;
|
||||
boolean flattenJmods = flattenEars || isEar;
|
||||
boolean flattenZips = flattenJmods || isJmod;
|
||||
|
||||
StringFunction fileNameFunction =
|
||||
new ListFunctionParser(
|
||||
new SingleFunctionParser(
|
||||
new FileNameParser(wildcardManager), wildcardManager)).parse(filter);
|
||||
// Set up the filtered jar writers.
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenZips, isZip, false, ".zip", zipFilter, null, false, null);
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenJmods, isJmod, false, ".jmod", jmodFilter, JMOD_HEADER, false, JMOD_PREFIXES);
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenEars, isEar, false, ".ear", earFilter, null, false, null);
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenWars, isWar, false, ".war", warFilter, null, false, WAR_PREFIXES);
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenAars, isAar, false, ".aar", aarFilter, null, false, null);
|
||||
|
||||
// Slight asymmetry: we filter plain class files beforehand,
|
||||
// but we filter and rename dex files and resource files after
|
||||
// creating and renaming them in the feature structure.
|
||||
// We therefore don't filter class files that go into dex
|
||||
// files.
|
||||
classWriter = new RenamedDataEntryWriter(fileNameFunction, classWriter);
|
||||
writer = new RenamedDataEntryWriter(fileNameFunction, writer);
|
||||
if (isAar)
|
||||
{
|
||||
// If we're writing an AAR, all input jars need to
|
||||
// be merged into a final classes.jar file or need to be put in the lib folder.
|
||||
if (mergeAarJars)
|
||||
{
|
||||
|
||||
writer =
|
||||
new FilteredDataEntryWriter(new DataEntryNameFilter(new ExtensionMatcher(".jar")),
|
||||
new RenamedDataEntryWriter(
|
||||
new ConstantStringFunction("classes.jar"), writer),
|
||||
writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer =
|
||||
new FilteredDataEntryWriter(new DataEntryNameFilter(new ExtensionMatcher(".jar")),
|
||||
new RenamedDataEntryWriter(string -> {
|
||||
String fileName = string.substring(string.lastIndexOf('/') + 1);
|
||||
if (fileName.equals("classes.jar"))
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "libs/" + fileName;
|
||||
}
|
||||
}, writer),
|
||||
writer);
|
||||
}
|
||||
}
|
||||
|
||||
writer = wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenJars, isJar, false, ".jar", jarFilter, null, false, null);
|
||||
|
||||
// Either we create an aab or apk; they can not be nested.
|
||||
writer = isAab ?
|
||||
wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenAabs, isAab, true, ".aab", aabFilter, null, false, null) :
|
||||
wrapInJarWriter(file, writer, extraDataEntryWriter, closeCachedJarWriter, flattenApks, isApk, false, ".apk", apkFilter, null, pageAlignNativeLibs, null);
|
||||
|
||||
// Create a writer for plain class files. Don't close the enclosed
|
||||
// writer through it, but let it be closed later on.
|
||||
DataEntryWriter classWriter =
|
||||
new ClassDataEntryWriter(programClassPool,
|
||||
new NonClosingDataEntryWriter(writer));
|
||||
|
||||
// Add a renaming filter, if specified.
|
||||
if (filter != null)
|
||||
{
|
||||
WildcardManager wildcardManager = new WildcardManager();
|
||||
|
||||
StringFunction fileNameFunction =
|
||||
new ListFunctionParser(
|
||||
new SingleFunctionParser(
|
||||
new FileNameParser(wildcardManager), wildcardManager)).parse(filter);
|
||||
|
||||
// Slight asymmetry: we filter plain class files beforehand,
|
||||
// but we filter and rename dex files and resource files after
|
||||
// creating and renaming them in the feature structure.
|
||||
// We therefore don't filter class files that go into dex
|
||||
// files.
|
||||
classWriter = new RenamedDataEntryWriter(fileNameFunction, classWriter);
|
||||
writer = new RenamedDataEntryWriter(fileNameFunction, writer);
|
||||
}
|
||||
|
||||
writer =
|
||||
// Filter on class files.
|
||||
new NameFilteredDataEntryWriter(
|
||||
new ExtensionMatcher(CLASS_FILE_EXTENSION),
|
||||
alternativeClassDataEntryWriterProvider != null ?
|
||||
alternativeClassDataEntryWriterProvider.apply(writer) :
|
||||
classWriter,
|
||||
writer);
|
||||
}
|
||||
|
||||
writer =
|
||||
// Filter on class files.
|
||||
new NameFilteredDataEntryWriter(
|
||||
new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION),
|
||||
classWriter,
|
||||
writer);
|
||||
|
||||
// Let the writer cascade, if specified.
|
||||
return alternativeWriter != null ?
|
||||
new CascadingDataEntryWriter(writer, alternativeWriter) :
|
||||
@@ -332,7 +393,7 @@ public class DataEntryWriterFactory
|
||||
boolean isJar,
|
||||
boolean isAab,
|
||||
String jarFilterExtension,
|
||||
List jarFilter,
|
||||
List<String> jarFilter,
|
||||
byte[] jarHeader,
|
||||
boolean pageAlignNativeLibs,
|
||||
String[][] prefixes)
|
||||
@@ -436,6 +497,7 @@ public class DataEntryWriterFactory
|
||||
DataEntryWriter zipWriter =
|
||||
new ZipWriter(uncompressedFilter,
|
||||
uncompressedAlignment,
|
||||
ENABLE_ZIP64_SUPPORT,
|
||||
pageAlignmentFilter,
|
||||
PAGE_ALIGNMENT,
|
||||
modificationTime,
|
||||
50
base/src/main/java/proguard/Dumper.java
Normal file
50
base/src/main/java/proguard/Dumper.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*/
|
||||
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.visitor.*;
|
||||
import proguard.pass.Pass;
|
||||
import proguard.util.PrintWriterUtil;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* This pass prints the contents of the program class pool.
|
||||
*
|
||||
* @author Tim Van Den Broecke
|
||||
*/
|
||||
public class Dumper implements Pass
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(Dumper.class);
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
public Dumper(Configuration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void execute(AppView appView) throws Exception
|
||||
{
|
||||
logger.info("Printing classes to [{}]...", PrintWriterUtil.fileName(configuration.dump));
|
||||
|
||||
PrintWriter pw = PrintWriterUtil.createPrintWriterOut(configuration.dump);
|
||||
try
|
||||
{
|
||||
appView.programClassPool.classesAccept(new ClassPrinter(pw));
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrintWriterUtil.closePrintWriter(configuration.dump, pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.util.*;
|
||||
import proguard.classfile.visitor.*;
|
||||
@@ -35,6 +37,7 @@ import java.util.*;
|
||||
public class FullyQualifiedClassNameChecker
|
||||
implements ClassVisitor
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(FullyQualifiedClassNameChecker.class);
|
||||
private static final String INVALID_CLASS_EXTENSION = ClassUtil.internalClassName(ClassConstants.CLASS_FILE_EXTENSION);
|
||||
|
||||
|
||||
@@ -194,7 +197,6 @@ implements ClassVisitor
|
||||
|
||||
public void visitAnyClass(Clazz clazz)
|
||||
{
|
||||
System.out.println(" Maybe you meant the fully qualified name '" +
|
||||
ClassUtil.externalClassName(clazz.getName()) + "'?");
|
||||
logger.info(" Maybe you meant the fully qualified name '{}'?", ClassUtil.externalClassName(clazz.getName()));
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,9 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
@@ -30,6 +33,8 @@ import java.util.*;
|
||||
*/
|
||||
public class GPL
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(GPL.class);
|
||||
|
||||
/**
|
||||
* Prints out a note about the GPL if ProGuard is linked against unknown
|
||||
* code.
|
||||
@@ -48,10 +53,10 @@ public class GPL
|
||||
{
|
||||
String uniquePackageNames = uniquePackageNames(unknownPackageNames);
|
||||
|
||||
System.out.println("ProGuard is released under the GNU General Public License. You therefore");
|
||||
System.out.println("must ensure that programs that link to it ("+uniquePackageNames+"...)");
|
||||
System.out.println("carry the GNU General Public License as well. Alternatively, you can");
|
||||
System.out.println("apply for an exception with the author of ProGuard.");
|
||||
logger.info("ProGuard is released under the GNU General Public License. You therefore");
|
||||
logger.info("must ensure that programs that link to it ({}...)", uniquePackageNames);
|
||||
logger.info("carry the GNU General Public License as well. Alternatively, you can");
|
||||
logger.info("apply for an exception with the author of ProGuard.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +165,7 @@ public class GPL
|
||||
private static boolean isKnown(String packageName)
|
||||
{
|
||||
return packageName.startsWith("java") ||
|
||||
packageName.startsWith("jdk.internal.reflect") ||
|
||||
packageName.startsWith("sun.reflect") ||
|
||||
packageName.startsWith("proguard") ||
|
||||
packageName.startsWith("org.apache.tools.ant") ||
|
||||
@@ -2,7 +2,7 @@
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
* Copyright (c) 2002-2022 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -20,49 +20,38 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.attribute.Attribute;
|
||||
import proguard.classfile.attribute.annotation.visitor.*;
|
||||
import proguard.classfile.attribute.visitor.*;
|
||||
import proguard.classfile.constant.visitor.AllConstantVisitor;
|
||||
import proguard.classfile.instruction.visitor.AllInstructionVisitor;
|
||||
import proguard.classfile.kotlin.*;
|
||||
import proguard.classfile.kotlin.visitor.ReferencedKotlinMetadataVisitor;
|
||||
import proguard.classfile.util.*;
|
||||
import proguard.classfile.util.kotlin.KotlinMetadataInitializer;
|
||||
import proguard.classfile.visitor.*;
|
||||
import proguard.resources.file.ResourceFilePool;
|
||||
import proguard.pass.Pass;
|
||||
import proguard.resources.file.visitor.ResourceJavaReferenceClassInitializer;
|
||||
import proguard.resources.kotlinmodule.util.KotlinModuleReferenceInitializer;
|
||||
import proguard.util.*;
|
||||
import proguard.util.kotlin.asserter.KotlinMetadataAsserter;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This class initializes class pools and resource information.
|
||||
* This pass initializes class pools and resource information.
|
||||
*
|
||||
* @author Eric Lafortune
|
||||
*/
|
||||
public class Initializer
|
||||
public class Initializer implements Pass
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(Initializer.class);
|
||||
private final Configuration configuration;
|
||||
private final boolean checkConfiguration;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new Initializer to initialize classes according to the given
|
||||
* configuration.
|
||||
*/
|
||||
public Initializer(Configuration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
|
||||
// Only check the configuration if either shrinking, optimization or obfuscation is enabled,
|
||||
// in other cases the configuration does not have any effect. This will speed up pure debug builds.
|
||||
this.checkConfiguration = configuration.shrink ||
|
||||
configuration.optimize ||
|
||||
configuration.obfuscate;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,26 +59,25 @@ public class Initializer
|
||||
* Initializes the classes in the given program class pool and library class
|
||||
* pool, performs some basic checks, and shrinks the library class pool.
|
||||
*/
|
||||
public void execute(ClassPool programClassPool,
|
||||
ClassPool libraryClassPool,
|
||||
ResourceFilePool resourceFilePool)
|
||||
throws IOException
|
||||
@Override
|
||||
public void execute(AppView appView) throws IOException
|
||||
{
|
||||
// We're using the system's default character encoding for writing to
|
||||
// the standard output and error output.
|
||||
PrintWriter out = new PrintWriter(System.out, true);
|
||||
PrintWriter err = new PrintWriter(System.err, true);
|
||||
logger.info("Initializing...");
|
||||
|
||||
int originalLibraryClassPoolSize = libraryClassPool.size();
|
||||
boolean checkConfiguration = configuration.shrink ||
|
||||
configuration.optimize ||
|
||||
configuration.obfuscate;
|
||||
|
||||
int originalLibraryClassPoolSize = appView.libraryClassPool.size();
|
||||
|
||||
// Perform basic checks on the configuration.
|
||||
WarningPrinter fullyQualifiedClassNameNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter fullyQualifiedClassNameNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
if (checkConfiguration)
|
||||
{
|
||||
FullyQualifiedClassNameChecker fullyQualifiedClassNameChecker =
|
||||
new FullyQualifiedClassNameChecker(programClassPool,
|
||||
libraryClassPool,
|
||||
new FullyQualifiedClassNameChecker(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
fullyQualifiedClassNameNotePrinter);
|
||||
|
||||
fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.keep);
|
||||
@@ -103,38 +91,38 @@ public class Initializer
|
||||
new ListParser(new NameParser()).parse(configuration.keepAttributes) :
|
||||
new EmptyStringMatcher();
|
||||
|
||||
WarningPrinter getAnnotationNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter getAnnotationNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
if (!keepAttributesMatcher.matches(Attribute.RUNTIME_VISIBLE_ANNOTATIONS))
|
||||
{
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new AllConstantVisitor(
|
||||
new GetAnnotationChecker(getAnnotationNotePrinter)));
|
||||
}
|
||||
|
||||
WarningPrinter getSignatureNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter getSignatureNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
if (!keepAttributesMatcher.matches(Attribute.SIGNATURE))
|
||||
{
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new AllConstantVisitor(
|
||||
new GetSignatureChecker(getSignatureNotePrinter)));
|
||||
}
|
||||
|
||||
WarningPrinter getEnclosingClassNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter getEnclosingClassNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
if (!keepAttributesMatcher.matches(Attribute.INNER_CLASSES))
|
||||
{
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new AllConstantVisitor(
|
||||
new GetEnclosingClassChecker(getEnclosingClassNotePrinter)));
|
||||
}
|
||||
|
||||
WarningPrinter getEnclosingMethodNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter getEnclosingMethodNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
if (!keepAttributesMatcher.matches(Attribute.ENCLOSING_METHOD))
|
||||
{
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new AllConstantVisitor(
|
||||
new GetEnclosingMethodChecker(getEnclosingMethodNotePrinter)));
|
||||
}
|
||||
@@ -146,49 +134,33 @@ public class Initializer
|
||||
ClassPool reducedLibraryClassPool = configuration.useUniqueClassMemberNames ?
|
||||
null : new ClassPool();
|
||||
|
||||
WarningPrinter classReferenceWarningPrinter = new WarningPrinter(err, configuration.warn);
|
||||
WarningPrinter dependencyWarningPrinter = new WarningPrinter(err, configuration.warn);
|
||||
WarningPrinter classReferenceWarningPrinter = new WarningLogger(logger, configuration.warn);
|
||||
WarningPrinter dependencyWarningPrinter = new WarningLogger(logger, configuration.warn);
|
||||
|
||||
// Initialize the superclass hierarchies for program classes.
|
||||
programClassPool.classesAccept(
|
||||
new ClassSuperHierarchyInitializer(programClassPool,
|
||||
libraryClassPool,
|
||||
appView.programClassPool.classesAccept(
|
||||
new ClassSuperHierarchyInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
classReferenceWarningPrinter,
|
||||
null));
|
||||
|
||||
// Initialize the superclass hierarchy of all library classes, without
|
||||
// warnings.
|
||||
libraryClassPool.classesAccept(
|
||||
new ClassSuperHierarchyInitializer(programClassPool,
|
||||
libraryClassPool,
|
||||
appView.libraryClassPool.classesAccept(
|
||||
new ClassSuperHierarchyInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
null,
|
||||
dependencyWarningPrinter));
|
||||
|
||||
WarningPrinter kotlinInitializationWarningPrinter = new WarningPrinter(err, configuration.warn);
|
||||
|
||||
// Initialize the Kotlin Metadata for Kotlin classes.
|
||||
if (configuration.keepKotlinMetadata)
|
||||
{
|
||||
ClassVisitor kotlinMetadataInitializer =
|
||||
new AllAttributeVisitor(
|
||||
new AttributeNameFilter(Attribute.RUNTIME_VISIBLE_ANNOTATIONS,
|
||||
new AllAnnotationVisitor(
|
||||
new AnnotationTypeFilter(KotlinConstants.TYPE_KOTLIN_METADATA,
|
||||
new KotlinMetadataInitializer(kotlinInitializationWarningPrinter)))));
|
||||
|
||||
programClassPool.classesAccept(kotlinMetadataInitializer);
|
||||
libraryClassPool.classesAccept(kotlinMetadataInitializer);
|
||||
}
|
||||
|
||||
// Initialize the class references of program class members and
|
||||
// attributes. Note that all superclass hierarchies have to be
|
||||
// initialized for this purpose.
|
||||
WarningPrinter programMemberReferenceWarningPrinter = new WarningPrinter(err, configuration.warn);
|
||||
WarningPrinter libraryMemberReferenceWarningPrinter = new WarningPrinter(err, configuration.warn);
|
||||
WarningPrinter programMemberReferenceWarningPrinter = new WarningLogger(logger, configuration.warn);
|
||||
WarningPrinter libraryMemberReferenceWarningPrinter = new WarningLogger(logger, configuration.warn);
|
||||
|
||||
programClassPool.classesAccept(
|
||||
new ClassReferenceInitializer(programClassPool,
|
||||
libraryClassPool,
|
||||
appView.programClassPool.classesAccept(
|
||||
new ClassReferenceInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
classReferenceWarningPrinter,
|
||||
programMemberReferenceWarningPrinter,
|
||||
libraryMemberReferenceWarningPrinter,
|
||||
@@ -196,54 +168,61 @@ public class Initializer
|
||||
|
||||
if (reducedLibraryClassPool != null)
|
||||
{
|
||||
if (configuration.keepKotlinMetadata)
|
||||
{
|
||||
// TODO(T16917): Improve this, so that only relevant classes are kept.
|
||||
appView.libraryClassPool.classesAccept(
|
||||
new ReferencedKotlinMetadataVisitor(
|
||||
(clazz, kotlinMetadata) -> clazz.accept(new ClassPoolFiller(reducedLibraryClassPool))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Collect the library classes that are directly referenced by
|
||||
// program classes, without reflection.
|
||||
programClassPool.classesAccept(
|
||||
new ReferencedClassVisitor(
|
||||
appView.programClassPool.classesAccept(
|
||||
new ReferencedClassVisitor(true,
|
||||
new LibraryClassFilter(
|
||||
new ClassPoolFiller(reducedLibraryClassPool))));
|
||||
|
||||
// Reinitialize the superclass hierarchies of referenced library
|
||||
// classes, this time with warnings.
|
||||
reducedLibraryClassPool.classesAccept(
|
||||
new ClassSuperHierarchyInitializer(programClassPool,
|
||||
libraryClassPool,
|
||||
new ClassSuperHierarchyInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
classReferenceWarningPrinter,
|
||||
null));
|
||||
}
|
||||
|
||||
// Initialize the enum annotation references.
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new AllAttributeVisitor(true,
|
||||
new AllElementValueVisitor(true,
|
||||
new EnumFieldReferenceInitializer())));
|
||||
|
||||
// Initialize the Class.forName references.
|
||||
WarningPrinter dynamicClassReferenceNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter classForNameNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter dynamicClassReferenceNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
WarningPrinter classForNameNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new AllMethodVisitor(
|
||||
new AllAttributeVisitor(
|
||||
new AllInstructionVisitor(
|
||||
new DynamicClassReferenceInitializer(programClassPool,
|
||||
libraryClassPool,
|
||||
new DynamicClassReferenceInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
dynamicClassReferenceNotePrinter,
|
||||
null,
|
||||
classForNameNotePrinter,
|
||||
createClassNoteExceptionMatcher(configuration.keep, true))))));
|
||||
|
||||
// Initialize the WebView.addJavascriptInterface references.
|
||||
WarningPrinter webViewClassReferenceNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
|
||||
// Initialize the Class.get[Declared]{Field,Method} references.
|
||||
WarningPrinter getMemberNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter getMemberNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new AllMethodVisitor(
|
||||
new AllAttributeVisitor(
|
||||
new DynamicMemberReferenceInitializer(programClassPool,
|
||||
libraryClassPool,
|
||||
new DynamicMemberReferenceInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
getMemberNotePrinter,
|
||||
createClassMemberNoteExceptionMatcher(configuration.keep, true),
|
||||
createClassMemberNoteExceptionMatcher(configuration.keep, false)))));
|
||||
@@ -251,11 +230,11 @@ public class Initializer
|
||||
// Initialize other string constant references, if requested.
|
||||
if (configuration.adaptClassStrings != null)
|
||||
{
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new ClassNameFilter(configuration.adaptClassStrings,
|
||||
new AllConstantVisitor(
|
||||
new StringReferenceInitializer(programClassPool,
|
||||
libraryClassPool))));
|
||||
new StringReferenceInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool))));
|
||||
}
|
||||
|
||||
// Initialize the class references of library class members.
|
||||
@@ -263,7 +242,7 @@ public class Initializer
|
||||
{
|
||||
// Collect the library classes that are referenced by program
|
||||
// classes, directly or indirectly, with or without reflection.
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new ReferencedClassVisitor(
|
||||
new LibraryClassFilter(
|
||||
new ClassHierarchyTraveler(true, true, true, false,
|
||||
@@ -273,15 +252,15 @@ public class Initializer
|
||||
// Initialize the class references of referenced library
|
||||
// classes, without warnings.
|
||||
reducedLibraryClassPool.classesAccept(
|
||||
new ClassReferenceInitializer(programClassPool,
|
||||
libraryClassPool,
|
||||
new ClassReferenceInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
dependencyWarningPrinter));
|
||||
|
||||
// Reset the library class pool.
|
||||
libraryClassPool.clear();
|
||||
appView.libraryClassPool.clear();
|
||||
|
||||
// Copy the library classes that are referenced directly by program
|
||||
// classes and the library classes that are referenced by referenced
|
||||
@@ -290,21 +269,21 @@ public class Initializer
|
||||
new MultiClassVisitor(
|
||||
new ClassHierarchyTraveler(true, true, true, false,
|
||||
new LibraryClassFilter(
|
||||
new ClassPoolFiller(libraryClassPool))),
|
||||
new ClassPoolFiller(appView.libraryClassPool))),
|
||||
|
||||
new ReferencedClassVisitor(
|
||||
new ReferencedClassVisitor(true,
|
||||
new LibraryClassFilter(
|
||||
new ClassHierarchyTraveler(true, true, true, false,
|
||||
new LibraryClassFilter(
|
||||
new ClassPoolFiller(libraryClassPool)))))
|
||||
new ClassPoolFiller(appView.libraryClassPool)))))
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize the class references of all library class members.
|
||||
libraryClassPool.classesAccept(
|
||||
new ClassReferenceInitializer(programClassPool,
|
||||
libraryClassPool,
|
||||
appView.libraryClassPool.classesAccept(
|
||||
new ClassReferenceInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@@ -316,34 +295,25 @@ public class Initializer
|
||||
ClassSubHierarchyInitializer classSubHierarchyInitializer =
|
||||
new ClassSubHierarchyInitializer();
|
||||
|
||||
programClassPool.accept(classSubHierarchyInitializer);
|
||||
libraryClassPool.accept(classSubHierarchyInitializer);
|
||||
appView.programClassPool.accept(classSubHierarchyInitializer);
|
||||
appView.libraryClassPool.accept(classSubHierarchyInitializer);
|
||||
|
||||
if (configuration.keepKotlinMetadata)
|
||||
{
|
||||
resourceFilePool.resourceFilesAccept(new KotlinModuleReferenceInitializer(programClassPool, libraryClassPool));
|
||||
|
||||
if (configuration.enableKotlinAsserter)
|
||||
{
|
||||
new KotlinMetadataAsserter()
|
||||
.execute(programClassPool,
|
||||
libraryClassPool,
|
||||
resourceFilePool,
|
||||
kotlinInitializationWarningPrinter);
|
||||
}
|
||||
appView.resourceFilePool.resourceFilesAccept(new KotlinModuleReferenceInitializer(appView.programClassPool, appView.libraryClassPool));
|
||||
}
|
||||
|
||||
// Share strings between the classes, to reduce heap memory usage.
|
||||
programClassPool.classesAccept(new StringSharer());
|
||||
libraryClassPool.classesAccept(new StringSharer());
|
||||
appView.programClassPool.classesAccept(new StringSharer());
|
||||
appView.libraryClassPool.classesAccept(new StringSharer());
|
||||
|
||||
// Check for any unmatched class members.
|
||||
WarningPrinter classMemberNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter classMemberNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
if (checkConfiguration)
|
||||
{
|
||||
ClassMemberChecker classMemberChecker =
|
||||
new ClassMemberChecker(programClassPool,
|
||||
new ClassMemberChecker(appView.programClassPool,
|
||||
classMemberNotePrinter);
|
||||
|
||||
classMemberChecker.checkClassSpecifications(configuration.keep);
|
||||
@@ -354,211 +324,197 @@ public class Initializer
|
||||
}
|
||||
|
||||
// Check for unkept descriptor classes of kept class members.
|
||||
WarningPrinter descriptorKeepNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter descriptorKeepNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
if (checkConfiguration)
|
||||
{
|
||||
new DescriptorKeepChecker(programClassPool,
|
||||
libraryClassPool,
|
||||
new DescriptorKeepChecker(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep);
|
||||
}
|
||||
|
||||
// Check for keep options that only match library classes.
|
||||
WarningPrinter libraryKeepNotePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter libraryKeepNotePrinter = new WarningLogger(logger, configuration.note);
|
||||
|
||||
if (checkConfiguration)
|
||||
{
|
||||
new LibraryKeepChecker(programClassPool,
|
||||
libraryClassPool,
|
||||
new LibraryKeepChecker(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
libraryKeepNotePrinter).checkClassSpecifications(configuration.keep);
|
||||
}
|
||||
|
||||
// Initialize the references to Java classes in resource files.
|
||||
resourceFilePool.resourceFilesAccept(
|
||||
new ResourceJavaReferenceClassInitializer(programClassPool));
|
||||
appView.resourceFilePool.resourceFilesAccept(
|
||||
new ResourceJavaReferenceClassInitializer(appView.programClassPool));
|
||||
|
||||
// Print out a summary of the notes, if necessary.
|
||||
int fullyQualifiedNoteCount = fullyQualifiedClassNameNotePrinter.getWarningCount();
|
||||
if (fullyQualifiedNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + fullyQualifiedNoteCount +
|
||||
" references to unknown classes.");
|
||||
out.println(" You should check your configuration for typos.");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unknownclass)");
|
||||
logger.info("Note: there were {} references to unknown classes.", fullyQualifiedNoteCount);
|
||||
logger.info(" You should check your configuration for typos.");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unknownclass)");
|
||||
}
|
||||
|
||||
int classMemberNoteCount = classMemberNotePrinter.getWarningCount();
|
||||
if (classMemberNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + classMemberNoteCount +
|
||||
" references to unknown class members.");
|
||||
out.println(" You should check your configuration for typos.");
|
||||
logger.info("Note: there were {} references to unknown class members.", classMemberNoteCount);
|
||||
logger.info(" You should check your configuration for typos.");
|
||||
}
|
||||
|
||||
int getAnnotationNoteCount = getAnnotationNotePrinter.getWarningCount();
|
||||
if (getAnnotationNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + getAnnotationNoteCount +
|
||||
" classes trying to access annotations using reflection.");
|
||||
out.println(" You should consider keeping the annotation attributes");
|
||||
out.println(" (using '-keepattributes *Annotation*').");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
|
||||
logger.info("Note: there were {} classes trying to access annotations using reflection.",
|
||||
getAnnotationNoteCount);
|
||||
logger.info(" You should consider keeping the annotation attributes");
|
||||
logger.info(" (using '-keepattributes *Annotation*').");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
|
||||
}
|
||||
|
||||
int getSignatureNoteCount = getSignatureNotePrinter.getWarningCount();
|
||||
if (getSignatureNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + getSignatureNoteCount +
|
||||
" classes trying to access generic signatures using reflection.");
|
||||
out.println(" You should consider keeping the signature attributes");
|
||||
out.println(" (using '-keepattributes Signature').");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
|
||||
logger.info("Note: there were {} classes trying to access generic signatures using reflection.",
|
||||
getSignatureNoteCount);
|
||||
logger.info(" You should consider keeping the signature attributes");
|
||||
logger.info(" (using '-keepattributes Signature').");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
|
||||
}
|
||||
|
||||
int getEnclosingClassNoteCount = getEnclosingClassNotePrinter.getWarningCount();
|
||||
if (getEnclosingClassNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + getEnclosingClassNoteCount +
|
||||
" classes trying to access enclosing classes using reflection.");
|
||||
out.println(" You should consider keeping the inner classes attributes");
|
||||
out.println(" (using '-keepattributes InnerClasses').");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
|
||||
logger.info("Note: there were {} classes trying to access enclosing classes using reflection.",
|
||||
getEnclosingClassNoteCount);
|
||||
logger.info(" You should consider keeping the inner classes attributes");
|
||||
logger.info(" (using '-keepattributes InnerClasses').");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
|
||||
}
|
||||
|
||||
int getEnclosingMethodNoteCount = getEnclosingMethodNotePrinter.getWarningCount();
|
||||
if (getEnclosingMethodNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + getEnclosingMethodNoteCount +
|
||||
" classes trying to access enclosing methods using reflection.");
|
||||
out.println(" You should consider keeping the enclosing method attributes");
|
||||
out.println(" (using '-keepattributes InnerClasses,EnclosingMethod').");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
|
||||
logger.info("Note: there were {} classes trying to access enclosing methods using reflection.",
|
||||
getEnclosingMethodNoteCount);
|
||||
logger.info(" You should consider keeping the enclosing method attributes");
|
||||
logger.info(" (using '-keepattributes InnerClasses,EnclosingMethod').");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
|
||||
}
|
||||
|
||||
int descriptorNoteCount = descriptorKeepNotePrinter.getWarningCount();
|
||||
if (descriptorNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + descriptorNoteCount +
|
||||
" unkept descriptor classes in kept class members.");
|
||||
out.println(" You should consider explicitly keeping the mentioned classes");
|
||||
out.println(" (using '-keep').");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#descriptorclass)");
|
||||
logger.info("Note: there were {} unkept descriptor classes in kept class members.",
|
||||
descriptorNoteCount);
|
||||
logger.info(" You should consider explicitly keeping the mentioned classes");
|
||||
logger.info(" (using '-keep').");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#descriptorclass)");
|
||||
}
|
||||
|
||||
int libraryNoteCount = libraryKeepNotePrinter.getWarningCount();
|
||||
if (libraryNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + libraryNoteCount +
|
||||
" library classes explicitly being kept.");
|
||||
out.println(" You don't need to keep library classes; they are already left unchanged.");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#libraryclass)");
|
||||
logger.info("Note: there were {} library classes explicitly being kept.", libraryNoteCount);
|
||||
logger.info(" You don't need to keep library classes; they are already left unchanged.");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#libraryclass)");
|
||||
}
|
||||
|
||||
int dynamicClassReferenceNoteCount = dynamicClassReferenceNotePrinter.getWarningCount();
|
||||
if (dynamicClassReferenceNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + dynamicClassReferenceNoteCount +
|
||||
" unresolved dynamic references to classes or interfaces.");
|
||||
out.println(" You should check if you need to specify additional program jars.");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dynamicalclass)");
|
||||
logger.info("Note: there were {} unresolved dynamic references to classes or interfaces.",
|
||||
dynamicClassReferenceNoteCount);
|
||||
logger.info(" You should check if you need to specify additional program jars.");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dynamicalclass)");
|
||||
}
|
||||
|
||||
int classForNameNoteCount = classForNameNotePrinter.getWarningCount();
|
||||
if (classForNameNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + classForNameNoteCount +
|
||||
" class casts of dynamically created class instances.");
|
||||
out.println(" You might consider explicitly keeping the mentioned classes and/or");
|
||||
out.println(" their implementations (using '-keep').");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dynamicalclasscast)");
|
||||
logger.info("Note: there were {} class casts of dynamically created class instances.",
|
||||
classForNameNoteCount);
|
||||
logger.info(" You might consider explicitly keeping the mentioned classes and/or");
|
||||
logger.info(" their implementations (using '-keep').");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dynamicalclasscast)");
|
||||
}
|
||||
|
||||
int getmemberNoteCount = getMemberNotePrinter.getWarningCount();
|
||||
if (getmemberNoteCount > 0)
|
||||
{
|
||||
out.println("Note: there were " + getmemberNoteCount +
|
||||
" accesses to class members by means of reflection.");
|
||||
out.println(" You should consider explicitly keeping the mentioned class members");
|
||||
out.println(" (using '-keep' or '-keepclassmembers').");
|
||||
out.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dynamicalclassmember)");
|
||||
logger.info("Note: there were {} accesses to class members by means of reflection.",
|
||||
getmemberNoteCount);
|
||||
logger.info(" You should consider explicitly keeping the mentioned class members");
|
||||
logger.info(" (using '-keep' or '-keepclassmembers').");
|
||||
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dynamicalclassmember)");
|
||||
}
|
||||
|
||||
// Print out a summary of the warnings, if necessary.
|
||||
int classReferenceWarningCount = classReferenceWarningPrinter.getWarningCount();
|
||||
if (classReferenceWarningCount > 0)
|
||||
{
|
||||
err.println("Warning: there were " + classReferenceWarningCount +
|
||||
" unresolved references to classes or interfaces.");
|
||||
err.println(" You may need to add missing library jars or update their versions.");
|
||||
err.println(" If your code works fine without the missing classes, you can suppress");
|
||||
err.println(" the warnings with '-dontwarn' options.");
|
||||
logger.warn("Warning: there were {} unresolved references to classes or interfaces.",
|
||||
classReferenceWarningCount);
|
||||
logger.warn(" You may need to add missing library jars or update their versions.");
|
||||
logger.warn(" If your code works fine without the missing classes, you can suppress");
|
||||
logger.warn(" the warnings with '-dontwarn' options.");
|
||||
|
||||
if (configuration.skipNonPublicLibraryClasses)
|
||||
{
|
||||
err.println(" You may also have to remove the option '-skipnonpubliclibraryclasses'.");
|
||||
logger.warn(" You may also have to remove the option '-skipnonpubliclibraryclasses'.");
|
||||
}
|
||||
|
||||
err.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unresolvedclass)");
|
||||
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unresolvedclass)");
|
||||
}
|
||||
|
||||
int dependencyWarningCount = dependencyWarningPrinter.getWarningCount();
|
||||
if (dependencyWarningCount > 0)
|
||||
{
|
||||
err.println("Warning: there were " + dependencyWarningCount +
|
||||
" instances of library classes depending on program classes.");
|
||||
err.println(" You must avoid such dependencies, since the program classes will");
|
||||
err.println(" be processed, while the library classes will remain unchanged.");
|
||||
err.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dependency)");
|
||||
logger.warn("Warning: there were {} instances of library classes depending on program classes.",
|
||||
dependencyWarningCount);
|
||||
logger.warn(" You must avoid such dependencies, since the program classes will");
|
||||
logger.warn(" be processed, while the library classes will remain unchanged.");
|
||||
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dependency)");
|
||||
}
|
||||
|
||||
int programMemberReferenceWarningCount = programMemberReferenceWarningPrinter.getWarningCount();
|
||||
if (programMemberReferenceWarningCount > 0)
|
||||
{
|
||||
err.println("Warning: there were " + programMemberReferenceWarningCount +
|
||||
" unresolved references to program class members.");
|
||||
err.println(" Your input classes appear to be inconsistent.");
|
||||
err.println(" You may need to recompile the code.");
|
||||
err.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unresolvedprogramclassmember)");
|
||||
logger.warn("Warning: there were {} unresolved references to program class members.",
|
||||
programMemberReferenceWarningCount);
|
||||
logger.warn(" Your input classes appear to be inconsistent.");
|
||||
logger.warn(" You may need to recompile the code.");
|
||||
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unresolvedprogramclassmember)");
|
||||
}
|
||||
|
||||
int libraryMemberReferenceWarningCount = libraryMemberReferenceWarningPrinter.getWarningCount();
|
||||
if (libraryMemberReferenceWarningCount > 0)
|
||||
{
|
||||
err.println("Warning: there were " + libraryMemberReferenceWarningCount +
|
||||
" unresolved references to library class members.");
|
||||
err.println(" You probably need to update the library versions.");
|
||||
logger.warn("Warning: there were {} unresolved references to library class members.",
|
||||
libraryMemberReferenceWarningCount);
|
||||
logger.warn(" You probably need to update the library versions.");
|
||||
|
||||
if (!configuration.skipNonPublicLibraryClassMembers)
|
||||
{
|
||||
err.println(" Alternatively, you may have to specify the option ");
|
||||
err.println(" '-dontskipnonpubliclibraryclassmembers'.");
|
||||
logger.warn(" Alternatively, you may have to specify the option ");
|
||||
logger.warn(" '-dontskipnonpubliclibraryclassmembers'.");
|
||||
}
|
||||
|
||||
if (configuration.skipNonPublicLibraryClasses)
|
||||
{
|
||||
err.println(" You may also have to remove the option '-skipnonpubliclibraryclasses'.");
|
||||
logger.warn(" You may also have to remove the option '-skipnonpubliclibraryclasses'.");
|
||||
}
|
||||
|
||||
err.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unresolvedlibraryclassmember)");
|
||||
}
|
||||
|
||||
if (configuration.keepKotlinMetadata)
|
||||
{
|
||||
int kotlinInitializationWarningCount = kotlinInitializationWarningPrinter.getWarningCount();
|
||||
|
||||
if (kotlinInitializationWarningCount > 0)
|
||||
{
|
||||
err.println("Warning: there were " + kotlinInitializationWarningCount +
|
||||
" errors during Kotlin metadata initialisation.");
|
||||
}
|
||||
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unresolvedlibraryclassmember)");
|
||||
}
|
||||
|
||||
boolean incompatibleOptimization = configuration.optimize && !configuration.shrink;
|
||||
|
||||
if (incompatibleOptimization)
|
||||
{
|
||||
err.println("Warning: optimization is enabled while shrinking is disabled.");
|
||||
err.println(" You need to remove the option -dontshrink or optimization might result in classes that fail verification at runtime.");
|
||||
logger.warn("Warning: optimization is enabled while shrinking is disabled.");
|
||||
logger.warn(" You need to remove the option -dontshrink or optimization might result in classes that fail verification at runtime.");
|
||||
}
|
||||
|
||||
if ((classReferenceWarningCount > 0 ||
|
||||
@@ -576,15 +532,21 @@ public class Initializer
|
||||
configuration.warn.isEmpty() ||
|
||||
configuration.ignoreWarnings))
|
||||
{
|
||||
out.println("Note: you're ignoring all warnings!");
|
||||
logger.info("Note: you're ignoring all warnings!");
|
||||
}
|
||||
|
||||
// Discard unused library classes.
|
||||
if (configuration.verbose)
|
||||
logger.info("Ignoring unused library classes...");
|
||||
logger.info(" Original number of library classes: {}", originalLibraryClassPoolSize);
|
||||
logger.info(" Final number of library classes: {}", appView.libraryClassPool.size());
|
||||
if (configuration.keepKotlinMetadata)
|
||||
{
|
||||
out.println("Ignoring unused library classes...");
|
||||
out.println(" Original number of library classes: " + originalLibraryClassPoolSize);
|
||||
out.println(" Final number of library classes: " + libraryClassPool.size());
|
||||
ClassCounter counter = new ClassCounter();
|
||||
appView.libraryClassPool.classesAccept(
|
||||
new ReferencedKotlinMetadataVisitor(
|
||||
(clazz, kotlinMetadata) -> clazz.accept(counter)
|
||||
)
|
||||
);
|
||||
logger.info(" Number of library classes with @kotlin.Metadata: {}", counter.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
* Copyright (c) 2002-2022 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -20,11 +20,13 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import proguard.classfile.*;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.kotlin.KotlinConstants;
|
||||
import proguard.classfile.util.*;
|
||||
import proguard.classfile.visitor.*;
|
||||
import proguard.io.*;
|
||||
import proguard.pass.Pass;
|
||||
import proguard.resources.file.*;
|
||||
import proguard.resources.file.io.ResourceFileDataEntryReader;
|
||||
import proguard.resources.file.visitor.*;
|
||||
@@ -32,18 +34,20 @@ import proguard.resources.kotlinmodule.io.KotlinModuleDataEntryReader;
|
||||
import proguard.util.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
|
||||
import static proguard.DataEntryReaderFactory.getFilterExcludingVersionedClasses;
|
||||
|
||||
/**
|
||||
* This class reads the input class files.
|
||||
* This pass reads the input class files.
|
||||
*
|
||||
* @author Eric Lafortune
|
||||
*/
|
||||
public class InputReader
|
||||
public class InputReader implements Pass
|
||||
{
|
||||
// Option to favor library classes over program classes, in case of
|
||||
// duplicates.
|
||||
// https://sourceforge.net/p/proguard/discussion/182455/thread/76430d9e
|
||||
private static final boolean FAVOR_LIBRARY_CLASSES = System.getProperty("favor.library.classes") != null;
|
||||
private static final Logger logger = LogManager.getLogger(InputReader.class);
|
||||
|
||||
private static final boolean DONT_READ_LIBRARY_KOTLIN_METADATA = System.getProperty("proguard.dontreadlibrarykotlinmetadata") != null;
|
||||
|
||||
|
||||
private final Configuration configuration;
|
||||
@@ -52,7 +56,6 @@ public class InputReader
|
||||
// feature names to classes and resource files.
|
||||
private String featureName;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new InputReader to read input class files as specified by the
|
||||
* given configuration.
|
||||
@@ -64,28 +67,24 @@ public class InputReader
|
||||
|
||||
|
||||
/**
|
||||
* Fills the given program class pool, library class pool, and resource file
|
||||
* Fills the program class pool, library class pool, and resource file
|
||||
* pool by reading files based on the current configuration.
|
||||
*/
|
||||
public void execute(ClassPool programClassPool,
|
||||
ClassPool libraryClassPool,
|
||||
ResourceFilePool resourceFilePool) throws IOException
|
||||
@Override
|
||||
public void execute(AppView appView) throws IOException
|
||||
{
|
||||
// We're using the system's default character encoding for writing to
|
||||
// the standard output and error output.
|
||||
PrintWriter out = new PrintWriter(System.out, true);
|
||||
PrintWriter err = new PrintWriter(System.err, true);
|
||||
logger.info("Reading input...");
|
||||
|
||||
WarningPrinter notePrinter = new WarningPrinter(out, configuration.note);
|
||||
WarningPrinter warningPrinter = new WarningPrinter(err, configuration.warn);
|
||||
WarningPrinter notePrinter = new WarningLogger(logger, configuration.note);
|
||||
WarningPrinter warningPrinter = new WarningLogger(logger, configuration.warn);
|
||||
|
||||
DuplicateClassPrinter duplicateClassPrinter = new DuplicateClassPrinter(notePrinter);
|
||||
DuplicateResourceFilePrinter duplicateResourceFilePrinter = new DuplicateResourceFilePrinter(notePrinter);
|
||||
|
||||
ClassVisitor classPoolFiller =
|
||||
new ClassPresenceFilter(programClassPool, duplicateClassPrinter,
|
||||
new ClassPresenceFilter(appView.programClassPool, duplicateClassPrinter,
|
||||
new MultiClassVisitor(
|
||||
new ClassPoolFiller(programClassPool),
|
||||
new ClassPoolFiller(appView.programClassPool),
|
||||
// Attach the current resource name, if any, to any program classes that it visits.
|
||||
new ProgramClassFilter(clazz -> clazz.setFeatureName(featureName))));
|
||||
|
||||
@@ -98,6 +97,7 @@ public class InputReader
|
||||
configuration.shrink ||
|
||||
configuration.optimize ||
|
||||
configuration.obfuscate,
|
||||
configuration.keepKotlinMetadata,
|
||||
warningPrinter,
|
||||
classPoolFiller);
|
||||
|
||||
@@ -113,9 +113,9 @@ public class InputReader
|
||||
// Create a visitor and a reader to fill the resource file pool with
|
||||
// plain resource file instances (while checking for duplicates).
|
||||
ResourceFileVisitor resourceFilePoolFiller =
|
||||
new ResourceFilePresenceFilter(resourceFilePool, duplicateResourceFilePrinter,
|
||||
new ResourceFilePresenceFilter(appView.resourceFilePool, duplicateResourceFilePrinter,
|
||||
new MultiResourceFileVisitor(
|
||||
new ResourceFilePoolFiller(resourceFilePool),
|
||||
new ResourceFilePoolFiller(appView.resourceFilePool),
|
||||
new MyResourceFileFeatureNameSetter()));
|
||||
|
||||
DataEntryReader resourceReader =
|
||||
@@ -138,7 +138,7 @@ public class InputReader
|
||||
resourceReader));
|
||||
|
||||
// Check if we have at least some input classes.
|
||||
if (programClassPool.size() == 0)
|
||||
if (appView.programClassPool.size() == 0)
|
||||
{
|
||||
throw new IOException("The input doesn't contain any classes. Did you specify the proper '-injars' options?");
|
||||
}
|
||||
@@ -161,35 +161,34 @@ public class InputReader
|
||||
configuration.skipNonPublicLibraryClasses,
|
||||
configuration.skipNonPublicLibraryClassMembers,
|
||||
true,
|
||||
!DONT_READ_LIBRARY_KOTLIN_METADATA && configuration.keepKotlinMetadata,
|
||||
warningPrinter,
|
||||
new ClassPresenceFilter(programClassPool, duplicateClassPrinter,
|
||||
new ClassPresenceFilter(libraryClassPool, duplicateClassPrinter,
|
||||
new ClassPoolFiller(libraryClassPool))))));
|
||||
new ClassPresenceFilter(appView.programClassPool, duplicateClassPrinter,
|
||||
new ClassPresenceFilter(appView.libraryClassPool, duplicateClassPrinter,
|
||||
new ClassPoolFiller(appView.libraryClassPool))))));
|
||||
}
|
||||
|
||||
// Print out a summary of the notes, if necessary.
|
||||
int noteCount = notePrinter.getWarningCount();
|
||||
if (noteCount > 0)
|
||||
{
|
||||
err.println("Note: there were " + noteCount +
|
||||
" duplicate class definitions.");
|
||||
err.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#duplicateclass)");
|
||||
logger.warn("Note: there were {} duplicate class definitions.", noteCount);
|
||||
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#duplicateclass)");
|
||||
}
|
||||
|
||||
// Print out a summary of the warnings, if necessary.
|
||||
int warningCount = warningPrinter.getWarningCount();
|
||||
if (warningCount > 0)
|
||||
{
|
||||
err.println("Warning: there were " + warningCount +
|
||||
" classes in incorrectly named files.");
|
||||
err.println(" You should make sure all file names correspond to their class names.");
|
||||
err.println(" The directory hierarchies must correspond to the package hierarchies.");
|
||||
err.println(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unexpectedclass)");
|
||||
logger.warn("Warning: there were {} classes in incorrectly named files.", warningCount);
|
||||
logger.warn(" You should make sure all file names correspond to their class names.");
|
||||
logger.warn(" The directory hierarchies must correspond to the package hierarchies.");
|
||||
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unexpectedclass)");
|
||||
|
||||
if (!configuration.ignoreWarnings)
|
||||
{
|
||||
err.println(" If you don't mind the mentioned classes not being written out,");
|
||||
err.println(" you could try your luck using the '-ignorewarnings' option.");
|
||||
logger.warn(" If you don't mind the mentioned classes not being written out,");
|
||||
logger.warn(" you could try your luck using the '-ignorewarnings' option.");
|
||||
throw new IOException("Please correct the above warnings first.");
|
||||
}
|
||||
}
|
||||
@@ -243,12 +242,29 @@ public class InputReader
|
||||
{
|
||||
try
|
||||
{
|
||||
List<String> filter = getFilterExcludingVersionedClasses(classPathEntry);
|
||||
|
||||
logger.info("{}{} [{}]{}",
|
||||
messagePrefix,
|
||||
classPathEntry.isDex() ? "dex" :
|
||||
classPathEntry.isApk() ? "apk" :
|
||||
classPathEntry.isAab() ? "aab" :
|
||||
classPathEntry.isJar() ? "jar" :
|
||||
classPathEntry.isAar() ? "aar" :
|
||||
classPathEntry.isWar() ? "war" :
|
||||
classPathEntry.isEar() ? "ear" :
|
||||
classPathEntry.isJmod() ? "jmod" :
|
||||
classPathEntry.isZip() ? "zip" :
|
||||
"directory",
|
||||
classPathEntry.getName(),
|
||||
filter != null || classPathEntry.isFiltered() ? " (filtered)" : ""
|
||||
);
|
||||
|
||||
// Create a reader that can unwrap jars, wars, ears, jmods and zips.
|
||||
DataEntryReader reader =
|
||||
new DataEntryReaderFactory(configuration.android)
|
||||
.createDataEntryReader(messagePrefix,
|
||||
classPathEntry,
|
||||
dataEntryReader);
|
||||
.createDataEntryReader(classPathEntry,
|
||||
dataEntryReader);
|
||||
|
||||
// Create the data entry source.
|
||||
DataEntrySource source =
|
||||
@@ -263,7 +279,7 @@ public class InputReader
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw (IOException)new IOException("Can't read [" + classPathEntry + "] (" + ex.getMessage() + ")").initCause(ex);
|
||||
throw new IOException("Can't read [" + classPathEntry + "] (" + ex.getMessage() + ")", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,49 @@ public class KeepClassSpecification extends ClassSpecification
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new KeepClassSpecification.
|
||||
*
|
||||
* @param markClassesAndMembers specifies whether to mark the classes.
|
||||
* @param markConditionally specifies whether to mark the classes and
|
||||
* class members conditionally. If true,
|
||||
* classes and class members are marked, on
|
||||
* the condition that all specified class
|
||||
* members are present.
|
||||
* @param markDescriptorClasses specifies whether to mark the classes in
|
||||
* the descriptors of the marked class members.
|
||||
* @param markCodeAttributes specified whether to mark the code attributes
|
||||
* of the marked class methods.
|
||||
* @param allowShrinking specifies whether shrinking is allowed.
|
||||
* @param allowOptimization specifies whether optimization is allowed.
|
||||
* @param allowObfuscation specifies whether obfuscation is allowed.
|
||||
* @param condition an optional extra condition.
|
||||
* @param classSpecification the specification of classes and class
|
||||
* members.
|
||||
*/
|
||||
@Deprecated
|
||||
public KeepClassSpecification(boolean markClassesAndMembers,
|
||||
boolean markConditionally,
|
||||
boolean markDescriptorClasses,
|
||||
boolean markCodeAttributes,
|
||||
boolean allowShrinking,
|
||||
boolean allowOptimization,
|
||||
boolean allowObfuscation,
|
||||
ClassSpecification condition,
|
||||
ClassSpecification classSpecification)
|
||||
{
|
||||
this(markClassesAndMembers,
|
||||
markClassesAndMembers,
|
||||
markConditionally,
|
||||
markDescriptorClasses,
|
||||
markCodeAttributes,
|
||||
allowShrinking,
|
||||
allowOptimization,
|
||||
allowObfuscation,
|
||||
condition,
|
||||
classSpecification);
|
||||
}
|
||||
|
||||
// Implementations for Object.
|
||||
|
||||
@Override
|
||||
@@ -151,20 +151,14 @@ extends ClassSpecificationVisitorFactory
|
||||
fieldVisitor = fieldVisitor == null ?
|
||||
new MemberDescriptorReferencedClassVisitor(classVisitor) :
|
||||
new MultiMemberVisitor(
|
||||
new MemberVisitor[]
|
||||
{
|
||||
fieldVisitor,
|
||||
new MemberDescriptorReferencedClassVisitor(classVisitor)
|
||||
});
|
||||
fieldVisitor,
|
||||
new MemberDescriptorReferencedClassVisitor(classVisitor));
|
||||
|
||||
methodVisitor = methodVisitor == null ?
|
||||
new MemberDescriptorReferencedClassVisitor(classVisitor) :
|
||||
new MemberDescriptorReferencedClassVisitor(true, classVisitor) :
|
||||
new MultiMemberVisitor(
|
||||
new MemberVisitor[]
|
||||
{
|
||||
methodVisitor,
|
||||
new MemberDescriptorReferencedClassVisitor(classVisitor)
|
||||
});
|
||||
methodVisitor,
|
||||
new MemberDescriptorReferencedClassVisitor(true, classVisitor));
|
||||
}
|
||||
|
||||
// Don't visit the classes if not specified.
|
||||
40
base/src/main/java/proguard/KotlinMetadataAdapter.java
Normal file
40
base/src/main/java/proguard/KotlinMetadataAdapter.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*/
|
||||
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.io.kotlin.KotlinMetadataWriter;
|
||||
import proguard.classfile.kotlin.KotlinMetadataVersion;
|
||||
import proguard.classfile.kotlin.visitor.ReferencedKotlinMetadataVisitor;
|
||||
import proguard.classfile.visitor.ClassCounter;
|
||||
import proguard.pass.Pass;
|
||||
|
||||
|
||||
public class KotlinMetadataAdapter
|
||||
implements Pass
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(KotlinMetadataAdapter.class);
|
||||
|
||||
|
||||
@Override
|
||||
public void execute(AppView appView)
|
||||
{
|
||||
logger.info("Adapting Kotlin metadata...");
|
||||
|
||||
ClassCounter counter = new ClassCounter();
|
||||
appView.programClassPool.classesAccept(
|
||||
new ReferencedKotlinMetadataVisitor(
|
||||
new KotlinMetadataWriter(
|
||||
(clazz, message) -> logger.warn(clazz.getName(), message),
|
||||
counter
|
||||
)));
|
||||
|
||||
logger.info(" Number of Kotlin classes adapted: " + counter.getCount());
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
* Copyright (c) 2002-2022 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -20,12 +20,16 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.ClassPool;
|
||||
import proguard.classfile.io.visitor.ProcessingFlagDataEntryFilter;
|
||||
import proguard.classfile.kotlin.KotlinConstants;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.configuration.ConfigurationLogger;
|
||||
import proguard.configuration.InitialStateInfo;
|
||||
import proguard.io.*;
|
||||
import proguard.pass.Pass;
|
||||
import proguard.resources.file.ResourceFilePool;
|
||||
import proguard.resources.file.util.ResourceFilePoolNameFunction;
|
||||
import proguard.resources.kotlinmodule.io.KotlinModuleDataEntryWriter;
|
||||
@@ -33,25 +37,23 @@ import proguard.util.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* This class writes the output class files and resource files, packaged in
|
||||
* This pass writes the output class files and resource files, packaged in
|
||||
* jar files, etc, if required.
|
||||
*
|
||||
* @author Eric Lafortune
|
||||
*/
|
||||
public class OutputWriter
|
||||
public class OutputWriter implements Pass
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(OutputWriter.class);
|
||||
private final Configuration configuration;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new OutputWriter to write output class files as specified by
|
||||
* the given configuration.
|
||||
*/
|
||||
public OutputWriter(Configuration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
@@ -62,10 +64,17 @@ public class OutputWriter
|
||||
* Writes the given class pool to class files, based on the current
|
||||
* configuration.
|
||||
*/
|
||||
public void execute(ClassPool programClassPool,
|
||||
ResourceFilePool resourceFilePool,
|
||||
ExtraDataEntryNameMap extraDataEntryNameMap) throws IOException
|
||||
@Override
|
||||
public void execute(AppView appView) throws IOException
|
||||
{
|
||||
logger.info("Writing output...");
|
||||
|
||||
if (configuration.addConfigurationDebugging)
|
||||
{
|
||||
logger.error("Warning: -addconfigurationdebugging is enabled; the resulting build will contain obfuscation information.");
|
||||
logger.error("It should only be used for debugging purposes.");
|
||||
}
|
||||
|
||||
ClassPath programJars = configuration.programJars;
|
||||
|
||||
// Construct a filter for files that shouldn't be compressed.
|
||||
@@ -89,14 +98,29 @@ public class OutputWriter
|
||||
|
||||
// Create a main data entry writer factory for all nested archives.
|
||||
DataEntryWriterFactory dataEntryWriterFactory =
|
||||
new DataEntryWriterFactory(programClassPool,
|
||||
resourceFilePool,
|
||||
new DataEntryWriterFactory(appView.programClassPool,
|
||||
appView.resourceFilePool,
|
||||
modificationTime,
|
||||
uncompressedFilter,
|
||||
configuration.zipAlign,
|
||||
configuration.android, //resourceInfo.pageAlignNativeLibs,
|
||||
configuration.obfuscate,
|
||||
privateKeyEntries);
|
||||
privateKeyEntries
|
||||
);
|
||||
|
||||
DataEntryWriter extraDataEntryWriter = null;
|
||||
if (configuration.extraJar != null)
|
||||
{
|
||||
// Extra data entries can optionally be written to a separate jar file.
|
||||
// This prevents duplicates if there are multiple -outjars that are later
|
||||
// combined together, after ProGuard processing.
|
||||
ClassPath extraClassPath = new ClassPath();
|
||||
extraClassPath.add(new ClassPathEntry(configuration.extraJar, true));
|
||||
log(extraClassPath, 0, 1, privateKeyEntries);
|
||||
extraDataEntryWriter =
|
||||
new UniqueDataEntryWriter(
|
||||
dataEntryWriterFactory.createDataEntryWriter(extraClassPath, 0, 1, null));
|
||||
}
|
||||
|
||||
int firstInputIndex = 0;
|
||||
int lastInputIndex = 0;
|
||||
@@ -119,11 +143,20 @@ public class OutputWriter
|
||||
if (nextIndex == programJars.size() ||
|
||||
!programJars.get(nextIndex).isOutput())
|
||||
{
|
||||
log(programJars, lastInputIndex + 1, nextIndex, privateKeyEntries);
|
||||
// Write the processed input entries to the output entries.
|
||||
writeOutput(dataEntryWriterFactory,
|
||||
programClassPool,
|
||||
resourceFilePool,
|
||||
extraDataEntryNameMap,
|
||||
configuration,
|
||||
appView.programClassPool,
|
||||
appView.initialStateInfo,
|
||||
appView.resourceFilePool,
|
||||
extraDataEntryWriter != null ?
|
||||
// The extraDataEntryWriter must be remain open
|
||||
// until all outputs have been written.
|
||||
new NonClosingDataEntryWriter(extraDataEntryWriter) :
|
||||
// no extraDataEntryWriter supplied
|
||||
null,
|
||||
appView.extraDataEntryNameMap,
|
||||
programJars,
|
||||
firstInputIndex,
|
||||
lastInputIndex + 1,
|
||||
@@ -134,8 +167,12 @@ public class OutputWriter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extraDataEntryWriter != null)
|
||||
{
|
||||
extraDataEntryWriter.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the private keys from the key stores, based on the given configuration.
|
||||
@@ -156,28 +193,6 @@ public class OutputWriter
|
||||
keyAliases == null ||
|
||||
keyPasswords == null)
|
||||
{
|
||||
// Print a note if any of the signing parameters have been
|
||||
// specified.
|
||||
if ((keyStoreFiles != null ||
|
||||
keyStorePasswords != null ||
|
||||
keyAliases != null ||
|
||||
keyPasswords != null) &&
|
||||
(configuration.note == null ||
|
||||
!configuration.note.isEmpty()))
|
||||
{
|
||||
StringBuffer missing = new StringBuffer();
|
||||
StringBuffer specified = new StringBuffer();
|
||||
|
||||
(keyStoreFiles == null ? missing : specified).append("a key store file, ");
|
||||
(keyStorePasswords == null ? missing : specified).append("a key store password, ");
|
||||
(keyAliases == null ? missing : specified).append("a key alias, ");
|
||||
(keyPasswords == null ? missing : specified).append("a key password, ");
|
||||
|
||||
System.out.println("Note: you've specified "+specified.toString());
|
||||
System.out.println(" but not "+missing.substring(0, missing.length()-2)+".");
|
||||
System.out.println(" You should specify the missing parameters to sign the output jars.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -190,7 +205,7 @@ public class OutputWriter
|
||||
KeyStore.PrivateKeyEntry[] privateKeys =
|
||||
new KeyStore.PrivateKeyEntry[keyCount];
|
||||
|
||||
Map certificates = new HashMap(keyCount);
|
||||
Map<X509Certificate, Integer> certificates = new HashMap<>(keyCount);
|
||||
|
||||
for (int index = 0; index < keyCount; index++)
|
||||
{
|
||||
@@ -209,10 +224,10 @@ public class OutputWriter
|
||||
// Check if the certificate accidentally is a duplicate,
|
||||
// to avoid basic configuration errors.
|
||||
X509Certificate certificate = (X509Certificate)privateKeyEntry.getCertificate();
|
||||
Integer duplicateIndex = (Integer)certificates.put(certificate, Integer.valueOf(index));
|
||||
Integer duplicateIndex = certificates.put(certificate, index);
|
||||
if (duplicateIndex != null)
|
||||
{
|
||||
throw new IllegalArgumentException("Duplicate specified signing certificates #"+(duplicateIndex.intValue()+1)+" and #"+(index+1)+" out of "+keyCount+" ["+certificate.getSubjectDN().getName()+"]");
|
||||
throw new IllegalArgumentException("Duplicate specified signing certificates #" + (duplicateIndex + 1) + " and #" + (index + 1) + " out of " + keyCount + " [" + certificate.getSubjectDN().getName() + "]");
|
||||
}
|
||||
|
||||
// Add the private key to the list.
|
||||
@@ -223,7 +238,7 @@ public class OutputWriter
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw (IOException)new IOException("Can't sign jar ("+e.getMessage()+")", e);
|
||||
throw new IOException("Can't sign jar ("+e.getMessage()+")", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,8 +276,11 @@ public class OutputWriter
|
||||
* Transfers the specified input jars to the specified output jars.
|
||||
*/
|
||||
private void writeOutput(DataEntryWriterFactory dataEntryWriterFactory,
|
||||
Configuration configuration,
|
||||
ClassPool programClassPool,
|
||||
InitialStateInfo initialStateInfo,
|
||||
ResourceFilePool resourceFilePool,
|
||||
DataEntryWriter extraDataEntryWriter,
|
||||
ExtraDataEntryNameMap extraDataEntryNameMap,
|
||||
ClassPath classPath,
|
||||
int fromInputIndex,
|
||||
@@ -284,15 +302,6 @@ public class OutputWriter
|
||||
toOutputIndex,
|
||||
null);
|
||||
|
||||
if (configuration.addConfigurationDebugging)
|
||||
{
|
||||
writer = new ExtraDataEntryWriter(ConfigurationLogger.CLASS_MAP_FILENAME,
|
||||
writer,
|
||||
new ClassMapDataEntryWriter(programClassPool, writer));
|
||||
System.err.println("Warning: -addconfigurationdebugging is enabled; the resulting build will contain obfuscation information.");
|
||||
System.err.println("It should only be used for debugging purposes.");
|
||||
}
|
||||
|
||||
DataEntryWriter resourceWriter = writer;
|
||||
|
||||
// Adapt plain resource file names that correspond to class names,
|
||||
@@ -312,9 +321,9 @@ public class OutputWriter
|
||||
{
|
||||
resourceWriter =
|
||||
new NameFilteredDataEntryWriter(KotlinConstants.MODULE.FILE_EXPRESSION,
|
||||
new FilteredDataEntryWriter(
|
||||
new ProcessingFlagDataEntryFilter(resourceFilePool, 0, ProcessingFlags.DONT_PROCESS_KOTLIN_MODULE),
|
||||
new KotlinModuleDataEntryWriter(resourceFilePool, resourceWriter)),
|
||||
new FilteredDataEntryWriter(
|
||||
new ProcessingFlagDataEntryFilter(resourceFilePool, 0, ProcessingFlags.DONT_PROCESS_KOTLIN_MODULE),
|
||||
new KotlinModuleDataEntryWriter(resourceFilePool, resourceWriter)),
|
||||
resourceWriter);
|
||||
}
|
||||
|
||||
@@ -339,7 +348,8 @@ public class OutputWriter
|
||||
if (configuration.obfuscate)
|
||||
{
|
||||
adaptingContentWriter =
|
||||
adaptResourceFiles(programClassPool,
|
||||
adaptResourceFiles(configuration,
|
||||
programClassPool,
|
||||
resourceWriter);
|
||||
}
|
||||
|
||||
@@ -352,18 +362,30 @@ public class OutputWriter
|
||||
|
||||
// Write any kept directories.
|
||||
DataEntryReader reader =
|
||||
writeDirectories(programClassPool,
|
||||
writeDirectories(configuration,
|
||||
programClassPool,
|
||||
resourceCopier,
|
||||
resourceRewriter);
|
||||
|
||||
// Trigger writing classes.
|
||||
// Write extra configuration files.
|
||||
reader =
|
||||
new ClassFilter(new IdleRewriter(writer),
|
||||
reader);
|
||||
writeExtraConfigurationFiles(configuration,
|
||||
programClassPool,
|
||||
initialStateInfo,
|
||||
extraDataEntryNameMap,
|
||||
reader,
|
||||
extraDataEntryWriter != null ? extraDataEntryWriter : writer);
|
||||
|
||||
// Inject any attached data entries.
|
||||
reader = new ExtraDataEntryReader(extraDataEntryNameMap,
|
||||
reader);
|
||||
// Write classes.
|
||||
DataEntryReader classReader = new ClassFilter(new IdleRewriter(writer), reader);
|
||||
|
||||
// Write classes attached as extra data entries.
|
||||
DataEntryReader extraClassReader = extraDataEntryWriter != null ?
|
||||
new ClassFilter(new IdleRewriter(extraDataEntryWriter), reader) :
|
||||
classReader;
|
||||
|
||||
// Write any attached data entries.
|
||||
reader = new ExtraDataEntryReader(extraDataEntryNameMap, classReader, extraClassReader);
|
||||
|
||||
// Go over the specified input entries and write their processed
|
||||
// versions.
|
||||
@@ -378,10 +400,36 @@ public class OutputWriter
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw (IOException)new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")").initCause(ex);
|
||||
String message = "Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")";
|
||||
throw new IOException(message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a resource writer that writes all extra configuration files to the given extra writer,
|
||||
* and delegates all other resources to the given resource writer.
|
||||
*/
|
||||
private DataEntryReader writeExtraConfigurationFiles(Configuration configuration,
|
||||
ClassPool programClassPool,
|
||||
InitialStateInfo initialStateInfo,
|
||||
ExtraDataEntryNameMap extraDataEntryNameMap,
|
||||
DataEntryReader resourceCopier,
|
||||
DataEntryWriter extraFileWriter)
|
||||
{
|
||||
if (configuration.addConfigurationDebugging)
|
||||
{
|
||||
extraDataEntryNameMap.addExtraDataEntry(ConfigurationLogger.CLASS_MAP_FILENAME);
|
||||
|
||||
resourceCopier =
|
||||
new NameFilteredDataEntryReader(ConfigurationLogger.CLASS_MAP_FILENAME,
|
||||
new ClassMapDataEntryReplacer(programClassPool, initialStateInfo,
|
||||
extraFileWriter),
|
||||
resourceCopier);
|
||||
}
|
||||
|
||||
return resourceCopier;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a writer that writes possibly renamed resource files to the
|
||||
@@ -390,8 +438,10 @@ public class OutputWriter
|
||||
private DataEntryWriter renameResourceFiles(ResourceFilePool resourceFilePool,
|
||||
DataEntryWriter dataEntryWriter)
|
||||
{
|
||||
return new RenamedDataEntryWriter(new ResourceFilePoolNameFunction(resourceFilePool),
|
||||
dataEntryWriter);
|
||||
return new FilteredDataEntryWriter(new DataEntryDirectoryFilter(),
|
||||
dataEntryWriter,
|
||||
new RenamedDataEntryWriter(new ResourceFilePoolNameFunction(resourceFilePool),
|
||||
dataEntryWriter));
|
||||
}
|
||||
|
||||
|
||||
@@ -400,12 +450,13 @@ public class OutputWriter
|
||||
* native libraries, text files) with shrunk, optimized, and obfuscated
|
||||
* contents to the given writer.
|
||||
*/
|
||||
private DataEntryReader adaptResourceFiles(ClassPool programClassPool,
|
||||
private DataEntryReader adaptResourceFiles(Configuration configuration,
|
||||
ClassPool programClassPool,
|
||||
DataEntryWriter writer)
|
||||
{
|
||||
// Pick a suitable encoding.
|
||||
Charset charset = configuration.android ?
|
||||
Charset.forName("UTF-8") :
|
||||
StandardCharsets.UTF_8 :
|
||||
Charset.defaultCharset();
|
||||
|
||||
// Filter between the various general resource files.
|
||||
@@ -420,7 +471,8 @@ public class OutputWriter
|
||||
* Writes possibly renamed directories that should be preserved to the
|
||||
* given resource copier, and non-directories to the given file copier.
|
||||
*/
|
||||
private DirectoryFilter writeDirectories(ClassPool programClassPool,
|
||||
private DirectoryFilter writeDirectories(Configuration configuration,
|
||||
ClassPool programClassPool,
|
||||
DataEntryReader directoryCopier,
|
||||
DataEntryReader fileCopier)
|
||||
{
|
||||
@@ -448,17 +500,17 @@ public class OutputWriter
|
||||
* Creates a map of old package prefixes to new package prefixes, based on
|
||||
* the given class pool.
|
||||
*/
|
||||
private static Map createPackagePrefixMap(ClassPool classPool)
|
||||
private static Map<String, String> createPackagePrefixMap(ClassPool classPool)
|
||||
{
|
||||
Map packagePrefixMap = new HashMap();
|
||||
Map<String, String> packagePrefixMap = new HashMap<>();
|
||||
|
||||
Iterator iterator = classPool.classNames();
|
||||
Iterator<String> iterator = classPool.classNames();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
String className = (String)iterator.next();
|
||||
String className = iterator.next();
|
||||
String packagePrefix = ClassUtil.internalPackagePrefix(className);
|
||||
|
||||
String mappedNewPackagePrefix = (String)packagePrefixMap.get(packagePrefix);
|
||||
String mappedNewPackagePrefix = packagePrefixMap.get(packagePrefix);
|
||||
if (mappedNewPackagePrefix == null ||
|
||||
!mappedNewPackagePrefix.equals(packagePrefix))
|
||||
{
|
||||
@@ -471,4 +523,30 @@ public class OutputWriter
|
||||
|
||||
return packagePrefixMap;
|
||||
}
|
||||
|
||||
|
||||
private static void log(ClassPath classPath, int fromIndex, int toIndex, KeyStore.PrivateKeyEntry[] privateKeyEntries)
|
||||
{
|
||||
for (int index = toIndex - 1; index >= fromIndex; index--)
|
||||
{
|
||||
ClassPathEntry classPathEntry = classPath.get(index);
|
||||
List<String> filter = DataEntryReaderFactory.getFilterExcludingVersionedClasses(classPathEntry);
|
||||
|
||||
logger.info("Preparing {}output {} [{}]{}",
|
||||
privateKeyEntries == null ? "" : "signed ",
|
||||
classPathEntry.isDex() ? "dex" :
|
||||
classPathEntry.isApk() ? "apk" :
|
||||
classPathEntry.isAab() ? "aab" :
|
||||
classPathEntry.isJar() ? "jar" :
|
||||
classPathEntry.isAar() ? "aar" :
|
||||
classPathEntry.isWar() ? "war" :
|
||||
classPathEntry.isEar() ? "ear" :
|
||||
classPathEntry.isJmod() ? "jmod" :
|
||||
classPathEntry.isZip() ? "zip" :
|
||||
"directory",
|
||||
classPathEntry.getName(),
|
||||
filter != null || classPathEntry.isFiltered() ? " (filtered)" : ""
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
660
base/src/main/java/proguard/ProGuard.java
Normal file
660
base/src/main/java/proguard/ProGuard.java
Normal file
@@ -0,0 +1,660 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.backport.Backporter;
|
||||
import proguard.classfile.editor.ClassElementSorter;
|
||||
import proguard.classfile.pass.PrimitiveArrayConstantIntroducer;
|
||||
import proguard.classfile.util.PrimitiveArrayConstantReplacer;
|
||||
import proguard.configuration.ConfigurationLoggingAdder;
|
||||
import proguard.configuration.InitialStateInfo;
|
||||
import proguard.evaluation.IncompleteClassHierarchyException;
|
||||
import proguard.logging.Logging;
|
||||
import proguard.mark.Marker;
|
||||
import proguard.normalize.StringNormalizer;
|
||||
import proguard.obfuscate.NameObfuscationReferenceFixer;
|
||||
import proguard.obfuscate.ObfuscationPreparation;
|
||||
import proguard.obfuscate.Obfuscator;
|
||||
import proguard.obfuscate.ResourceFileNameAdapter;
|
||||
import proguard.optimize.LineNumberTrimmer;
|
||||
import proguard.optimize.Optimizer;
|
||||
import proguard.optimize.gson.GsonOptimizer;
|
||||
import proguard.optimize.peephole.LineNumberLinearizer;
|
||||
import proguard.pass.PassRunner;
|
||||
import proguard.preverify.PreverificationClearer;
|
||||
import proguard.preverify.Preverifier;
|
||||
import proguard.preverify.SubroutineInliner;
|
||||
import proguard.shrink.Shrinker;
|
||||
import proguard.strip.KotlinAnnotationStripper;
|
||||
import proguard.util.ConstantMatcher;
|
||||
import proguard.util.ListParser;
|
||||
import proguard.util.NameParser;
|
||||
import proguard.util.StringMatcher;
|
||||
import proguard.util.kotlin.KotlinUnsupportedVersionChecker;
|
||||
import proguard.util.kotlin.asserter.KotlinMetadataVerifier;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Tool for shrinking, optimizing, obfuscating, and preverifying Java classes.
|
||||
*
|
||||
* @author Eric Lafortune
|
||||
*/
|
||||
public class ProGuard
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(ProGuard.class);
|
||||
public static final String VERSION = "ProGuard, version " + getVersion();
|
||||
|
||||
/**
|
||||
* A data object containing pass inputs in a centralized location. Passes can access and update the information
|
||||
* at any point in the pipeline.
|
||||
*/
|
||||
private final AppView appView;
|
||||
private final PassRunner passRunner;
|
||||
private final Configuration configuration;
|
||||
|
||||
/**
|
||||
* Creates a new ProGuard object to process jars as specified by the given
|
||||
* configuration.
|
||||
*/
|
||||
public ProGuard(Configuration configuration)
|
||||
{
|
||||
this.appView = new AppView();
|
||||
this.passRunner = new PassRunner();
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs all subsequent ProGuard operations.
|
||||
*/
|
||||
public void execute() throws Exception
|
||||
{
|
||||
Logging.configureVerbosity(configuration.verbose);
|
||||
|
||||
logger.always().log(VERSION);
|
||||
|
||||
try
|
||||
{
|
||||
checkGpl();
|
||||
|
||||
// Set the -keepkotlinmetadata option if necessary.
|
||||
if (!configuration.dontProcessKotlinMetadata)
|
||||
{
|
||||
configuration.keepKotlinMetadata = requiresKotlinMetadata();
|
||||
}
|
||||
|
||||
if (configuration.printConfiguration != null)
|
||||
{
|
||||
printConfiguration();
|
||||
}
|
||||
|
||||
checkConfiguration();
|
||||
|
||||
if (configuration.programJars.hasOutput())
|
||||
{
|
||||
checkUpToDate();
|
||||
}
|
||||
|
||||
if (configuration.targetClassVersion != 0)
|
||||
{
|
||||
configuration.backport = true;
|
||||
}
|
||||
|
||||
readInput();
|
||||
|
||||
if (configuration.shrink ||
|
||||
configuration.optimize ||
|
||||
configuration.obfuscate ||
|
||||
configuration.preverify)
|
||||
{
|
||||
clearPreverification();
|
||||
}
|
||||
|
||||
if (configuration.printSeeds != null ||
|
||||
configuration.backport ||
|
||||
configuration.shrink ||
|
||||
configuration.optimize ||
|
||||
configuration.obfuscate ||
|
||||
configuration.preverify ||
|
||||
configuration.addConfigurationDebugging ||
|
||||
configuration.keepKotlinMetadata)
|
||||
{
|
||||
initialize();
|
||||
mark();
|
||||
}
|
||||
|
||||
checkConfigurationAfterInitialization();
|
||||
|
||||
if (configuration.addConfigurationDebugging)
|
||||
{
|
||||
// Remember the initial state of the program classpool and resource filepool
|
||||
// before shrinking / obfuscation / optimization.
|
||||
appView.initialStateInfo = new InitialStateInfo(appView.programClassPool);
|
||||
}
|
||||
|
||||
if (configuration.keepKotlinMetadata)
|
||||
{
|
||||
stripKotlinMetadataAnnotations();
|
||||
}
|
||||
|
||||
if (configuration.optimize ||
|
||||
configuration.obfuscate)
|
||||
{
|
||||
introducePrimitiveArrayConstants();
|
||||
}
|
||||
|
||||
if (configuration.backport)
|
||||
{
|
||||
backport();
|
||||
}
|
||||
|
||||
if (configuration.addConfigurationDebugging)
|
||||
{
|
||||
addConfigurationLogging();
|
||||
}
|
||||
|
||||
if (configuration.printSeeds != null)
|
||||
{
|
||||
printSeeds();
|
||||
}
|
||||
|
||||
if (configuration.preverify ||
|
||||
configuration.android)
|
||||
{
|
||||
inlineSubroutines();
|
||||
}
|
||||
|
||||
if (configuration.shrink)
|
||||
{
|
||||
shrink(false);
|
||||
}
|
||||
|
||||
// Create a matcher for filtering optimizations.
|
||||
StringMatcher filter = configuration.optimizations != null ?
|
||||
new ListParser(new NameParser()).parse(configuration.optimizations) :
|
||||
new ConstantMatcher(true);
|
||||
|
||||
if (configuration.optimize &&
|
||||
filter.matches(Optimizer.LIBRARY_GSON))
|
||||
{
|
||||
optimizeGson();
|
||||
}
|
||||
|
||||
if (configuration.optimize)
|
||||
{
|
||||
optimize();
|
||||
linearizeLineNumbers();
|
||||
}
|
||||
|
||||
if (configuration.obfuscate)
|
||||
{
|
||||
obfuscate();
|
||||
}
|
||||
|
||||
if (configuration.keepKotlinMetadata)
|
||||
{
|
||||
adaptKotlinMetadata();
|
||||
}
|
||||
|
||||
if (configuration.optimize ||
|
||||
configuration.obfuscate)
|
||||
{
|
||||
expandPrimitiveArrayConstants();
|
||||
normalizeStrings();
|
||||
}
|
||||
|
||||
if (configuration.targetClassVersion != 0)
|
||||
{
|
||||
target();
|
||||
}
|
||||
|
||||
if (configuration.preverify)
|
||||
{
|
||||
preverify();
|
||||
}
|
||||
|
||||
// Trim line numbers after preverification as this might
|
||||
// also remove some instructions.
|
||||
if (configuration.optimize ||
|
||||
configuration.preverify)
|
||||
{
|
||||
trimLineNumbers();
|
||||
}
|
||||
|
||||
if (configuration.shrink ||
|
||||
configuration.optimize ||
|
||||
configuration.obfuscate ||
|
||||
configuration.preverify)
|
||||
{
|
||||
sortClassElements();
|
||||
}
|
||||
|
||||
if (configuration.programJars.hasOutput())
|
||||
{
|
||||
writeOutput();
|
||||
}
|
||||
|
||||
if (configuration.dump != null)
|
||||
{
|
||||
dump();
|
||||
}
|
||||
}
|
||||
catch (UpToDateChecker.UpToDateException ignore) {}
|
||||
catch (IncompleteClassHierarchyException e)
|
||||
{
|
||||
throw new RuntimeException(
|
||||
System.lineSeparator() + System.lineSeparator() +
|
||||
"It appears you are missing some classes resulting in an incomplete class hierarchy, " + System.lineSeparator() +
|
||||
"please refer to the troubleshooting page in the manual: " + System.lineSeparator() +
|
||||
"https://www.guardsquare.com/en/products/proguard/manual/troubleshooting#superclass" + System.lineSeparator()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeStrings() throws Exception {
|
||||
passRunner.run(new StringNormalizer(),appView);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the GPL.
|
||||
*/
|
||||
private void checkGpl()
|
||||
{
|
||||
GPL.check();
|
||||
}
|
||||
|
||||
private boolean requiresKotlinMetadata()
|
||||
{
|
||||
return configuration.keepKotlinMetadata ||
|
||||
(configuration.keep != null &&
|
||||
configuration.keep.stream().anyMatch(
|
||||
keepClassSpecification -> ! keepClassSpecification.allowObfuscation &&
|
||||
! keepClassSpecification.allowShrinking &&
|
||||
"kotlin/Metadata".equals(keepClassSpecification.className)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints out the configuration that ProGuard is using.
|
||||
*/
|
||||
private void printConfiguration() throws IOException
|
||||
{
|
||||
try (ConfigurationWriter configurationWriter = new ConfigurationWriter(configuration.printConfiguration))
|
||||
{
|
||||
configurationWriter.write(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the configuration for conflicts and inconsistencies.
|
||||
*/
|
||||
private void checkConfiguration() throws IOException
|
||||
{
|
||||
new ConfigurationVerifier(configuration).check();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the output is up-to-date.
|
||||
*/
|
||||
private void checkUpToDate()
|
||||
{
|
||||
new UpToDateChecker(configuration).check();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the input class files.
|
||||
*/
|
||||
private void readInput() throws Exception
|
||||
{
|
||||
// Fill the program class pool and the library class pool.
|
||||
passRunner.run(new InputReader(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears any JSE preverification information from the program classes.
|
||||
*/
|
||||
private void clearPreverification() throws Exception
|
||||
{
|
||||
passRunner.run(new PreverificationClearer(), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the cross-references between all classes, performs some
|
||||
* basic checks, and shrinks the library class pool.
|
||||
*/
|
||||
private void initialize() throws Exception
|
||||
{
|
||||
if (configuration.keepKotlinMetadata)
|
||||
{
|
||||
passRunner.run(new KotlinUnsupportedVersionChecker(), appView);
|
||||
}
|
||||
passRunner.run(new Initializer(configuration), appView);
|
||||
|
||||
verifyKotlinMetadata();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Marks the classes, class members and attributes to be kept or encrypted,
|
||||
* by setting the appropriate access flags.
|
||||
*/
|
||||
private void mark() throws Exception
|
||||
{
|
||||
passRunner.run(new Marker(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Strips the Kotlin metadata annotation where possible.
|
||||
*/
|
||||
private void stripKotlinMetadataAnnotations() throws Exception
|
||||
{
|
||||
passRunner.run(new KotlinAnnotationStripper(configuration), appView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the configuration after it has been initialized.
|
||||
*/
|
||||
private void checkConfigurationAfterInitialization() throws Exception
|
||||
{
|
||||
passRunner.run(new AfterInitConfigurationVerifier(configuration), appView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces primitive array initialization code by primitive array constants.
|
||||
*/
|
||||
private void introducePrimitiveArrayConstants() throws Exception
|
||||
{
|
||||
passRunner.run(new PrimitiveArrayConstantIntroducer(), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Backports java language features to the specified target version.
|
||||
*/
|
||||
private void backport() throws Exception
|
||||
{
|
||||
passRunner.run(new Backporter(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds configuration logging code, providing suggestions on improving
|
||||
* the ProGuard configuration.
|
||||
*/
|
||||
private void addConfigurationLogging() throws Exception
|
||||
{
|
||||
passRunner.run(new ConfigurationLoggingAdder(), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints out classes and class members that are used as seeds in the
|
||||
* shrinking and obfuscation steps.
|
||||
*/
|
||||
private void printSeeds() throws Exception
|
||||
{
|
||||
passRunner.run(new SeedPrinter(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs the subroutine inlining step.
|
||||
*/
|
||||
private void inlineSubroutines() throws Exception
|
||||
{
|
||||
// Perform the actual inlining.
|
||||
passRunner.run(new SubroutineInliner(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs the shrinking step.
|
||||
*/
|
||||
private void shrink(boolean afterOptimizer) throws Exception
|
||||
{
|
||||
// Perform the actual shrinking.
|
||||
passRunner.run(new Shrinker(configuration, afterOptimizer), appView);
|
||||
|
||||
verifyKotlinMetadata();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Optimizes usages of the Gson library.
|
||||
*/
|
||||
private void optimizeGson() throws Exception
|
||||
{
|
||||
// Perform the Gson optimization.
|
||||
passRunner.run(new GsonOptimizer(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs the optimization step.
|
||||
*/
|
||||
private void optimize() throws Exception
|
||||
{
|
||||
Optimizer optimizer = new Optimizer(configuration);
|
||||
|
||||
for (int optimizationPass = 0; optimizationPass < configuration.optimizationPasses; optimizationPass++)
|
||||
{
|
||||
// Perform the actual optimization.
|
||||
passRunner.run(optimizer, appView);
|
||||
|
||||
// Shrink again, if we may.
|
||||
if (configuration.shrink)
|
||||
{
|
||||
shrink(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disambiguates the line numbers of all program classes, after
|
||||
* optimizations like method inlining and class merging.
|
||||
*/
|
||||
private void linearizeLineNumbers() throws Exception
|
||||
{
|
||||
passRunner.run(new LineNumberLinearizer(), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs the obfuscation step.
|
||||
*/
|
||||
private void obfuscate() throws Exception
|
||||
{
|
||||
passRunner.run(new ObfuscationPreparation(configuration), appView);
|
||||
|
||||
// Perform the actual obfuscation.
|
||||
passRunner.run(new Obfuscator(configuration), appView);
|
||||
|
||||
// Adapt resource file names that correspond to class names, if necessary.
|
||||
if (configuration.adaptResourceFileNames != null)
|
||||
{
|
||||
passRunner.run(new ResourceFileNameAdapter(configuration), appView);
|
||||
}
|
||||
|
||||
// Fix the Kotlin modules so the filename matches and the class names match.
|
||||
passRunner.run(new NameObfuscationReferenceFixer(configuration), appView);
|
||||
|
||||
verifyKotlinMetadata();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapts Kotlin Metadata annotations.
|
||||
*/
|
||||
private void adaptKotlinMetadata() throws Exception
|
||||
{
|
||||
passRunner.run(new KotlinMetadataAdapter(), appView);
|
||||
}
|
||||
|
||||
private void verifyKotlinMetadata() throws Exception {
|
||||
if (configuration.keepKotlinMetadata &&
|
||||
configuration.enableKotlinAsserter)
|
||||
{
|
||||
passRunner.run(new KotlinMetadataVerifier(configuration), appView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands primitive array constants back to traditional primitive array
|
||||
* initialization code.
|
||||
*/
|
||||
private void expandPrimitiveArrayConstants()
|
||||
{
|
||||
appView.programClassPool.classesAccept(new PrimitiveArrayConstantReplacer());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets that target versions of the program classes.
|
||||
*/
|
||||
private void target() throws Exception
|
||||
{
|
||||
passRunner.run(new Targeter(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs the preverification step.
|
||||
*/
|
||||
private void preverify() throws Exception
|
||||
{
|
||||
// Perform the actual preverification.
|
||||
passRunner.run(new Preverifier(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Trims the line number table attributes of all program classes.
|
||||
*/
|
||||
private void trimLineNumbers() throws Exception
|
||||
{
|
||||
passRunner.run(new LineNumberTrimmer(), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sorts the elements of all program classes.
|
||||
*/
|
||||
private void sortClassElements()
|
||||
{
|
||||
appView.programClassPool.classesAccept(
|
||||
new ClassElementSorter(
|
||||
/* sortInterfaces = */ true,
|
||||
/* sortConstants = */ true,
|
||||
// Sorting members can cause problems with code such as clazz.getMethods()[1]
|
||||
/* sortMembers = */ false,
|
||||
// PGD-192: Sorting attributes can cause problems for some compilers
|
||||
/* sortAttributes = */ false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes the output class files.
|
||||
*/
|
||||
private void writeOutput() throws Exception
|
||||
{
|
||||
// Write out the program class pool.
|
||||
passRunner.run(new OutputWriter(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints out the contents of the program classes.
|
||||
*/
|
||||
private void dump() throws Exception
|
||||
{
|
||||
passRunner.run(new Dumper(configuration), appView);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the implementation version from the manifest.
|
||||
*/
|
||||
public static String getVersion()
|
||||
{
|
||||
Package pack = ProGuard.class.getPackage();
|
||||
if (pack != null)
|
||||
{
|
||||
String version = pack.getImplementationVersion();
|
||||
if (version != null)
|
||||
{
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
return "undefined";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The main method for ProGuard.
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
if (args.length == 0)
|
||||
{
|
||||
logger.warn(VERSION);
|
||||
logger.warn("Usage: java proguard.ProGuard [options ...]");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Create the default options.
|
||||
Configuration configuration = new Configuration();
|
||||
|
||||
try
|
||||
{
|
||||
// Parse the options specified in the command line arguments.
|
||||
try (ConfigurationParser parser = new ConfigurationParser(args, System.getProperties()))
|
||||
{
|
||||
parser.parse(configuration);
|
||||
}
|
||||
|
||||
// Execute ProGuard with these options.
|
||||
new ProGuard(configuration).execute();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error("Unexpected error", ex);
|
||||
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
102
base/src/main/java/proguard/SeedPrinter.java
Normal file
102
base/src/main/java/proguard/SeedPrinter.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.visitor.*;
|
||||
import proguard.optimize.*;
|
||||
import proguard.pass.Pass;
|
||||
import proguard.util.PrintWriterUtil;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* This pass prints out the seeds specified by keep options.
|
||||
*
|
||||
* @author Eric Lafortune
|
||||
*/
|
||||
public class SeedPrinter implements Pass
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(SeedPrinter.class);
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
public SeedPrinter(Configuration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints out the seeds for the classes in the given program class pool.
|
||||
*
|
||||
* @throws IOException if an IO error occurs while writing the configuration.
|
||||
*/
|
||||
@Override
|
||||
public void execute(AppView appView) throws IOException
|
||||
{
|
||||
logger.info("Printing kept classes, fields, and methods...");
|
||||
|
||||
PrintWriter printWriter = PrintWriterUtil.createPrintWriterOut(configuration.printSeeds);
|
||||
|
||||
try
|
||||
{
|
||||
// Check if we have at least some keep commands.
|
||||
if (configuration.keep == null)
|
||||
{
|
||||
throw new IOException("You have to specify '-keep' options if you want to write out kept elements with '-printseeds'.");
|
||||
}
|
||||
|
||||
// Clean up any old processing info.
|
||||
appView.programClassPool.classesAccept(new ClassCleaner());
|
||||
appView.libraryClassPool.classesAccept(new ClassCleaner());
|
||||
|
||||
// Create a visitor for printing out the seeds. We're printing out
|
||||
// the program elements that are preserved against shrinking,
|
||||
// optimization, or obfuscation.
|
||||
KeepMarker keepMarker = new KeepMarker();
|
||||
ClassPoolVisitor classPoolvisitor =
|
||||
new KeepClassSpecificationVisitorFactory(true, true, true)
|
||||
.createClassPoolVisitor(configuration.keep,
|
||||
keepMarker,
|
||||
keepMarker,
|
||||
keepMarker,
|
||||
null);
|
||||
|
||||
// Mark the seeds.
|
||||
appView.programClassPool.accept(classPoolvisitor);
|
||||
appView.libraryClassPool.accept(classPoolvisitor);
|
||||
|
||||
// Print out the seeds.
|
||||
SimpleClassPrinter printer = new SimpleClassPrinter(false, printWriter);
|
||||
appView.programClassPool.classesAcceptAlphabetically(
|
||||
new MultiClassVisitor(
|
||||
new KeptClassFilter(printer),
|
||||
new AllMemberVisitor(new KeptMemberFilter(printer))
|
||||
));
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrintWriterUtil.closePrintWriter(configuration.printSeeds, printWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,27 +20,26 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.ClassPool;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.classfile.visitor.ClassVersionSetter;
|
||||
import proguard.pass.Pass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This class sets the target version on program classes.
|
||||
* This pass sets the target version on program classes.
|
||||
*
|
||||
* @author Eric Lafortune
|
||||
*/
|
||||
public class Targeter
|
||||
public class Targeter implements Pass
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(Targeter.class);
|
||||
private final Configuration configuration;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new Targeter to set the target version on program classes
|
||||
* according to the given configuration.
|
||||
*/
|
||||
public Targeter(Configuration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
@@ -50,37 +49,40 @@ public class Targeter
|
||||
/**
|
||||
* Sets the target version on classes in the given program class pool.
|
||||
*/
|
||||
public void execute(ClassPool programClassPool) throws IOException
|
||||
@Override
|
||||
public void execute(AppView appView) throws IOException
|
||||
{
|
||||
logger.info("Setting target versions...");
|
||||
|
||||
Set newerClassVersions = configuration.warn != null ? null : new HashSet();
|
||||
|
||||
programClassPool.classesAccept(new ClassVersionSetter(configuration.targetClassVersion,
|
||||
newerClassVersions));
|
||||
appView.programClassPool.classesAccept(new ClassVersionSetter(configuration.targetClassVersion,
|
||||
newerClassVersions));
|
||||
|
||||
if (newerClassVersions != null &&
|
||||
newerClassVersions.size() > 0)
|
||||
{
|
||||
System.err.print("Warning: some classes have more recent versions (");
|
||||
logger.error("Warning: some classes have more recent versions (");
|
||||
|
||||
Iterator iterator = newerClassVersions.iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
Integer classVersion = (Integer)iterator.next();
|
||||
System.err.print(ClassUtil.externalClassVersion(classVersion.intValue()));
|
||||
logger.error(ClassUtil.externalClassVersion(classVersion.intValue()));
|
||||
|
||||
if (iterator.hasNext())
|
||||
{
|
||||
System.err.print(",");
|
||||
logger.error(",");
|
||||
}
|
||||
}
|
||||
|
||||
System.err.println(")");
|
||||
System.err.println(" than the target version ("+ClassUtil.externalClassVersion(configuration.targetClassVersion)+").");
|
||||
logger.error(")");
|
||||
logger.error(" than the target version ({}).", ClassUtil.externalClassVersion(configuration.targetClassVersion));
|
||||
|
||||
if (!configuration.ignoreWarnings)
|
||||
{
|
||||
System.err.println(" If you are sure this is not a problem,");
|
||||
System.err.println(" you could try your luck using the '-ignorewarnings' option.");
|
||||
logger.error(" If you are sure this is not a problem,");
|
||||
logger.error(" you could try your luck using the '-ignorewarnings' option.");
|
||||
throw new IOException("Please correct the above warnings first.");
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,9 @@
|
||||
*/
|
||||
package proguard;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.*;
|
||||
|
||||
@@ -30,6 +33,7 @@ import java.net.*;
|
||||
*/
|
||||
public class UpToDateChecker
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(UpToDateChecker.class);
|
||||
private final Configuration configuration;
|
||||
|
||||
|
||||
@@ -46,7 +50,7 @@ public class UpToDateChecker
|
||||
* Returns whether the output is up to date, based on the modification times
|
||||
* of the input jars, output jars, and library jars (or directories).
|
||||
*/
|
||||
public boolean check()
|
||||
public void check() throws UpToDateException
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -99,12 +103,12 @@ public class UpToDateChecker
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
// The output is outdated.
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("The output seems up to date");
|
||||
logger.always().log("The output seems up to date");
|
||||
|
||||
return true;
|
||||
throw new UpToDateException();
|
||||
}
|
||||
|
||||
|
||||
@@ -248,4 +252,9 @@ public class UpToDateChecker
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This Exception is thrown when the output is up-to-date.
|
||||
*/
|
||||
public static class UpToDateException extends RuntimeException {}
|
||||
}
|
||||
@@ -20,6 +20,8 @@
|
||||
*/
|
||||
package proguard.backport;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.attribute.*;
|
||||
import proguard.classfile.attribute.annotation.*;
|
||||
@@ -59,7 +61,7 @@ implements ClassVisitor,
|
||||
AnnotationVisitor,
|
||||
ElementValueVisitor
|
||||
{
|
||||
private static final boolean DEBUG = false;
|
||||
private static final Logger logger = LogManager.getFormatterLogger(AbstractAPIConverter.class);
|
||||
|
||||
private final ClassPool programClassPool;
|
||||
private final ClassPool libraryClassPool;
|
||||
@@ -801,17 +803,15 @@ implements ClassVisitor,
|
||||
new ConstantInstruction(replacementInstructionOpcode,
|
||||
methodConstant));
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println(String.format("Replacing instruction at offset %d: %s.%s%s -> %s.%s%s",
|
||||
offset,
|
||||
anyMethodrefConstant.getClassName(clazz),
|
||||
anyMethodrefConstant.getName(clazz),
|
||||
anyMethodrefConstant.getType(clazz),
|
||||
className,
|
||||
methodName,
|
||||
methodDesc));
|
||||
}
|
||||
logger.debug("Replacing instruction at offset %d: %s.%s%s -> %s.%s%s",
|
||||
offset,
|
||||
anyMethodrefConstant.getClassName(clazz),
|
||||
anyMethodrefConstant.getName(clazz),
|
||||
anyMethodrefConstant.getType(clazz),
|
||||
className,
|
||||
methodName,
|
||||
methodDesc
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
*/
|
||||
package proguard.backport;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.*;
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.attribute.Attribute;
|
||||
@@ -30,43 +32,36 @@ import proguard.classfile.instruction.Instruction;
|
||||
import proguard.classfile.instruction.visitor.InstructionCounter;
|
||||
import proguard.classfile.util.*;
|
||||
import proguard.classfile.visitor.*;
|
||||
import proguard.io.ExtraDataEntryNameMap;
|
||||
import proguard.util.MultiValueMap;
|
||||
import proguard.pass.Pass;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class backports classes to the specified targetClassVersion.
|
||||
* This pass backports classes to the specified targetClassVersion.
|
||||
*
|
||||
* @author Thomas Neidhart
|
||||
*/
|
||||
public class Backporter
|
||||
public class Backporter implements Pass
|
||||
{
|
||||
private static final Logger logger = LogManager.getLogger(Backporter.class);
|
||||
private final Configuration configuration;
|
||||
|
||||
|
||||
public Backporter(Configuration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
public void execute(ClassPool programClassPool,
|
||||
ClassPool libraryClassPool,
|
||||
ExtraDataEntryNameMap extraDataEntryNameMap)
|
||||
@Override
|
||||
public void execute(AppView appView) throws IOException
|
||||
{
|
||||
int targetClassVersion = configuration.targetClassVersion;
|
||||
|
||||
if (configuration.verbose)
|
||||
{
|
||||
System.out.println("Backporting class files...");
|
||||
}
|
||||
|
||||
PrintWriter err = new PrintWriter(System.err, true);
|
||||
logger.info("Backporting class files...");
|
||||
|
||||
// Clean up any previous processing info.
|
||||
programClassPool.classesAccept(new ClassCleaner());
|
||||
libraryClassPool.classesAccept(new ClassCleaner());
|
||||
appView.programClassPool.classesAccept(new ClassCleaner());
|
||||
appView.libraryClassPool.classesAccept(new ClassCleaner());
|
||||
|
||||
final InstructionCounter replacedStringConcatCounter = new InstructionCounter();
|
||||
final ClassCounter lambdaExpressionCounter = new ClassCounter();
|
||||
@@ -80,7 +75,7 @@ public class Backporter
|
||||
{
|
||||
// Convert indy string concatenations to StringBuilder chains
|
||||
CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true);
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new ClassVersionFilter(VersionConstants.CLASS_VERSION_1_9,
|
||||
new AllAttributeVisitor(
|
||||
new AttributeNameFilter(Attribute.BOOTSTRAP_METHODS,
|
||||
@@ -98,7 +93,7 @@ public class Backporter
|
||||
new BootstrapMethodsAttributeShrinker(),
|
||||
|
||||
// Initialize new references to StringBuilder.
|
||||
new ClassReferenceInitializer(programClassPool, libraryClassPool)
|
||||
new ClassReferenceInitializer(appView.programClassPool, appView.libraryClassPool)
|
||||
))))));
|
||||
}
|
||||
|
||||
@@ -107,7 +102,7 @@ public class Backporter
|
||||
// Collect all classes with BootstrapMethod attributes,
|
||||
// and convert lambda expressions and method references.
|
||||
ClassPool filteredClasses = new ClassPool();
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new ClassVersionFilter(VersionConstants.CLASS_VERSION_1_8,
|
||||
new AllAttributeVisitor(
|
||||
new AttributeNameFilter(Attribute.BOOTSTRAP_METHODS,
|
||||
@@ -119,16 +114,16 @@ public class Backporter
|
||||
filteredClasses.classesAccept(
|
||||
new MultiClassVisitor(
|
||||
// Replace the indy instructions related to lambda expressions.
|
||||
new LambdaExpressionConverter(programClassPool,
|
||||
libraryClassPool,
|
||||
extraDataEntryNameMap,
|
||||
new LambdaExpressionConverter(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
appView.extraDataEntryNameMap,
|
||||
lambdaExpressionCounter),
|
||||
|
||||
// Clean up unused bootstrap methods and their dangling constants.
|
||||
new BootstrapMethodsAttributeShrinker(),
|
||||
|
||||
// Re-initialize references.
|
||||
new ClassReferenceInitializer(programClassPool, libraryClassPool)
|
||||
new ClassReferenceInitializer(appView.programClassPool, appView.libraryClassPool)
|
||||
));
|
||||
|
||||
// Remove static and default methods from interfaces if the
|
||||
@@ -137,7 +132,7 @@ public class Backporter
|
||||
// does not explicitly mention static interface methods, although
|
||||
// they seem to work correctly.
|
||||
ClassPool interfaceClasses = new ClassPool();
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new ClassVersionFilter(VersionConstants.CLASS_VERSION_1_8,
|
||||
new ClassAccessFilter(AccessConstants.INTERFACE, 0,
|
||||
new ClassPoolFiller(interfaceClasses))));
|
||||
@@ -148,9 +143,9 @@ public class Backporter
|
||||
|
||||
interfaceClasses.classesAccept(
|
||||
new MultiClassVisitor(
|
||||
new StaticInterfaceMethodConverter(programClassPool,
|
||||
libraryClassPool,
|
||||
extraDataEntryNameMap,
|
||||
new StaticInterfaceMethodConverter(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
appView.extraDataEntryNameMap,
|
||||
modifiedClassCollector,
|
||||
staticInterfaceMethodCounter),
|
||||
|
||||
@@ -160,16 +155,16 @@ public class Backporter
|
||||
|
||||
// Re-Initialize references in modified classes.
|
||||
modifiedClasses.classesAccept(
|
||||
new ClassReferenceInitializer(programClassPool,
|
||||
libraryClassPool));
|
||||
new ClassReferenceInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool));
|
||||
}
|
||||
|
||||
if (targetClassVersion < VersionConstants.CLASS_VERSION_1_7)
|
||||
{
|
||||
// Replace / remove method calls only available in Java 7+.
|
||||
InstructionSequenceBuilder ____ =
|
||||
new InstructionSequenceBuilder(programClassPool,
|
||||
libraryClassPool);
|
||||
new InstructionSequenceBuilder(appView.programClassPool,
|
||||
appView.libraryClassPool);
|
||||
|
||||
Instruction[][][] instructions = new Instruction[][][]
|
||||
{
|
||||
@@ -206,7 +201,7 @@ public class Backporter
|
||||
|
||||
CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
|
||||
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
new AllMethodVisitor(
|
||||
new AllAttributeVisitor(
|
||||
new PeepholeEditor(null, codeAttributeEditor,
|
||||
@@ -227,39 +222,39 @@ public class Backporter
|
||||
new ClassNameFilter("java8/**",
|
||||
streamSupportClasses);
|
||||
|
||||
programClassPool.classesAccept(streamSupportVisitor);
|
||||
libraryClassPool.classesAccept(streamSupportVisitor);
|
||||
appView.programClassPool.classesAccept(streamSupportVisitor);
|
||||
appView.libraryClassPool.classesAccept(streamSupportVisitor);
|
||||
|
||||
if (streamSupportClasses.getCount() > 0)
|
||||
{
|
||||
WarningPrinter streamSupportWarningPrinter =
|
||||
new WarningPrinter(err, configuration.warn);
|
||||
new WarningLogger(logger, configuration.warn);
|
||||
|
||||
ClassPool modifiedClasses = new ClassPool();
|
||||
ClassVisitor modifiedClassCollector =
|
||||
new ClassPoolFiller(modifiedClasses);
|
||||
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
// Do not process classes of the stream support library itself.
|
||||
new ClassNameFilter("!java8/**",
|
||||
new StreamSupportConverter(programClassPool,
|
||||
libraryClassPool,
|
||||
new StreamSupportConverter(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
streamSupportWarningPrinter,
|
||||
modifiedClassCollector,
|
||||
replacedStreamsMethodCallCounter)));
|
||||
|
||||
// Re-Initialize references in modified classes.
|
||||
modifiedClasses.classesAccept(
|
||||
new ClassReferenceInitializer(programClassPool,
|
||||
libraryClassPool));
|
||||
new ClassReferenceInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool));
|
||||
|
||||
int conversionWarningCount = streamSupportWarningPrinter.getWarningCount();
|
||||
if (conversionWarningCount > 0)
|
||||
{
|
||||
err.println("Warning: there were " + conversionWarningCount +
|
||||
" Java 8 stream API method calls that could not be backported.");
|
||||
err.println(" You should check if a your project setup is correct (compileSdkVersion, streamsupport dependency).");
|
||||
err.println(" For more information, consult the section \'Integration->Gradle Plugin->Java 8 stream API support\' in our manual");
|
||||
logger.warn("Warning: there were {} Java 8 stream API method calls that could not be backported.",
|
||||
conversionWarningCount);
|
||||
logger.warn(" You should check if a your project setup is correct (compileSdkVersion, streamsupport dependency).");
|
||||
logger.warn(" For more information, consult the section \'Integration->Gradle Plugin->Java 8 stream API support\' in our manual");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,39 +269,39 @@ public class Backporter
|
||||
new ClassNameFilter("org/threeten/bp/**",
|
||||
threetenClasses);
|
||||
|
||||
programClassPool.classesAccept(threetenClassVisitor);
|
||||
libraryClassPool.classesAccept(threetenClassVisitor);
|
||||
appView.programClassPool.classesAccept(threetenClassVisitor);
|
||||
appView.libraryClassPool.classesAccept(threetenClassVisitor);
|
||||
|
||||
if (threetenClasses.getCount() > 0)
|
||||
{
|
||||
WarningPrinter threetenWarningPrinter =
|
||||
new WarningPrinter(err, configuration.warn);
|
||||
new WarningLogger(logger, configuration.warn);
|
||||
|
||||
ClassPool modifiedClasses = new ClassPool();
|
||||
ClassVisitor modifiedClassCollector =
|
||||
new ClassPoolFiller(modifiedClasses);
|
||||
|
||||
programClassPool.classesAccept(
|
||||
appView.programClassPool.classesAccept(
|
||||
// Do not process classes of the threeten library itself.
|
||||
new ClassNameFilter("!org/threeten/bp/**",
|
||||
new JSR310Converter(programClassPool,
|
||||
libraryClassPool,
|
||||
new JSR310Converter(appView.programClassPool,
|
||||
appView.libraryClassPool,
|
||||
threetenWarningPrinter,
|
||||
modifiedClassCollector,
|
||||
replacedTimeMethodCallCounter)));
|
||||
|
||||
// Re-Initialize references in modified classes.
|
||||
modifiedClasses.classesAccept(
|
||||
new ClassReferenceInitializer(programClassPool,
|
||||
libraryClassPool));
|
||||
new ClassReferenceInitializer(appView.programClassPool,
|
||||
appView.libraryClassPool));
|
||||
|
||||
int conversionWarningCount = threetenWarningPrinter.getWarningCount();
|
||||
if (conversionWarningCount > 0)
|
||||
{
|
||||
err.println("Warning: there were " + conversionWarningCount +
|
||||
" Java 8 time API method calls that could not be backported.");
|
||||
err.println(" You should check if a your project setup is correct (compileSdkVersion, threetenbp dependency).");
|
||||
err.println(" For more information, consult the section \'Integration->Gradle Plugin->Java 8 time API support\' in our manual");
|
||||
logger.warn("Warning: there were {} Java 8 time API method calls that could not be backported.",
|
||||
conversionWarningCount);
|
||||
logger.warn(" You should check if a your project setup is correct (compileSdkVersion, threetenbp dependency).");
|
||||
logger.warn(" For more information, consult the section \'Integration->Gradle Plugin->Java 8 time API support\' in our manual");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -316,18 +311,21 @@ public class Backporter
|
||||
// Set the class version of all classes in the program ClassPool
|
||||
// to the specified target version. This is needed to perform
|
||||
// optimization on the backported + generated classes.
|
||||
programClassPool.classesAccept(new ClassVersionSetter(targetClassVersion));
|
||||
appView.programClassPool.classesAccept(new ClassVersionSetter(targetClassVersion));
|
||||
}
|
||||
|
||||
if (configuration.verbose)
|
||||
// Backporting may introduce access issues, for example related to nest members/host.
|
||||
if (configuration.allowAccessModification)
|
||||
{
|
||||
System.out.println(" Number of converted string concatenations: " + replacedStringConcatCounter.getCount());
|
||||
System.out.println(" Number of converted lambda expressions: " + lambdaExpressionCounter.getCount());
|
||||
System.out.println(" Number of converted static interface methods: " + staticInterfaceMethodCounter.getCount());
|
||||
System.out.println(" Number of converted default interface methods: " + defaultInterfaceMethodCounter.getCount());
|
||||
System.out.println(" Number of replaced Java 7+ method calls: " + replacedMethodCallCounter.getCount());
|
||||
System.out.println(" Number of replaced Java 8 stream method calls: " + replacedStreamsMethodCallCounter.getCount());
|
||||
System.out.println(" Number of replaced Java 8 time method calls: " + replacedTimeMethodCallCounter.getCount());
|
||||
appView.programClassPool.classesAccept(new AccessFixer());
|
||||
}
|
||||
|
||||
logger.info(" Number of converted string concatenations: {}", replacedStringConcatCounter.getCount());
|
||||
logger.info(" Number of converted lambda expressions: {}", lambdaExpressionCounter.getCount());
|
||||
logger.info(" Number of converted static interface methods: {}", staticInterfaceMethodCounter.getCount());
|
||||
logger.info(" Number of converted default interface methods: {}", defaultInterfaceMethodCounter.getCount());
|
||||
logger.info(" Number of replaced Java 7+ method calls: {}", replacedMethodCallCounter.getCount());
|
||||
logger.info(" Number of replaced Java 8 stream method calls: {}", replacedStreamsMethodCallCounter.getCount());
|
||||
logger.info(" Number of replaced Java 8 time method calls: {}", replacedTimeMethodCallCounter.getCount());
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
* Copyright (c) 2002-2022 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -20,18 +20,51 @@
|
||||
*/
|
||||
package proguard.backport;
|
||||
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.attribute.*;
|
||||
import proguard.classfile.attribute.visitor.*;
|
||||
import proguard.classfile.constant.*;
|
||||
import proguard.classfile.editor.*;
|
||||
import proguard.classfile.instruction.*;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.AccessConstants;
|
||||
import proguard.classfile.ClassConstants;
|
||||
import proguard.classfile.ClassPool;
|
||||
import proguard.classfile.Clazz;
|
||||
import proguard.classfile.Member;
|
||||
import proguard.classfile.Method;
|
||||
import proguard.classfile.ProgramClass;
|
||||
import proguard.classfile.ProgramMethod;
|
||||
import proguard.classfile.TypeConstants;
|
||||
import proguard.classfile.VersionConstants;
|
||||
import proguard.classfile.attribute.Attribute;
|
||||
import proguard.classfile.attribute.CodeAttribute;
|
||||
import proguard.classfile.attribute.visitor.AllAttributeVisitor;
|
||||
import proguard.classfile.attribute.visitor.AttributeVisitor;
|
||||
import proguard.classfile.constant.InvokeDynamicConstant;
|
||||
import proguard.classfile.constant.MethodHandleConstant;
|
||||
import proguard.classfile.editor.ClassBuilder;
|
||||
import proguard.classfile.editor.CodeAttributeEditor;
|
||||
import proguard.classfile.editor.CompactCodeAttributeComposer;
|
||||
import proguard.classfile.editor.ConstantPoolEditor;
|
||||
import proguard.classfile.editor.InstructionSequenceBuilder;
|
||||
import proguard.classfile.editor.MemberRemover;
|
||||
import proguard.classfile.instruction.ConstantInstruction;
|
||||
import proguard.classfile.instruction.Instruction;
|
||||
import proguard.classfile.instruction.visitor.InstructionVisitor;
|
||||
import proguard.classfile.util.*;
|
||||
import proguard.classfile.visitor.*;
|
||||
import proguard.classfile.util.ClassReferenceInitializer;
|
||||
import proguard.classfile.util.ClassSubHierarchyInitializer;
|
||||
import proguard.classfile.util.ClassSuperHierarchyInitializer;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.classfile.util.InternalTypeEnumeration;
|
||||
import proguard.classfile.visitor.AllMethodVisitor;
|
||||
import proguard.classfile.visitor.ClassVisitor;
|
||||
import proguard.classfile.visitor.MemberAccessFlagSetter;
|
||||
import proguard.classfile.visitor.MemberAccessSetter;
|
||||
import proguard.classfile.visitor.MemberVisitor;
|
||||
import proguard.classfile.visitor.MultiClassVisitor;
|
||||
import proguard.io.ExtraDataEntryNameMap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This ClassVisitor converts all lambda expressions in the visited
|
||||
@@ -47,11 +80,7 @@ implements ClassVisitor,
|
||||
AttributeVisitor,
|
||||
InstructionVisitor
|
||||
{
|
||||
//*
|
||||
private static final boolean DEBUG = false;
|
||||
/*/
|
||||
public static boolean DEBUG = System.getProperty("lec") != null;
|
||||
//*/
|
||||
private static final Logger logger = LogManager.getLogger(LambdaExpressionConverter.class);
|
||||
|
||||
|
||||
private static final String LAMBDA_SINGLETON_FIELD_NAME = "INSTANCE";
|
||||
@@ -76,7 +105,7 @@ implements ClassVisitor,
|
||||
this.extraDataEntryNameMap = extraDataEntryNameMap;
|
||||
this.extraClassVisitor = extraClassVisitor;
|
||||
|
||||
this.lambdaExpressionMap = new HashMap<Integer, LambdaExpression>();
|
||||
this.lambdaExpressionMap = new HashMap<>();
|
||||
this.codeAttributeEditor = new CodeAttributeEditor(true, true);
|
||||
this.memberRemover = new MemberRemover();
|
||||
}
|
||||
@@ -96,10 +125,7 @@ implements ClassVisitor,
|
||||
|
||||
if (!lambdaExpressionMap.isEmpty())
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: converting lambda expressions in ["+programClass.getName()+"]");
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: converting lambda expressions in [{}]", programClass.getName());
|
||||
|
||||
for (LambdaExpression lambdaExpression : lambdaExpressionMap.values())
|
||||
{
|
||||
@@ -187,10 +213,7 @@ implements ClassVisitor,
|
||||
|
||||
if (lambdaExpression.isStateless())
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: "+constantInstruction.toString(offset)+" -> getting static "+lambdaClassName+"."+LAMBDA_SINGLETON_FIELD_NAME);
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: {} -> getting static {}.{}", constantInstruction.toString(offset), lambdaClassName, LAMBDA_SINGLETON_FIELD_NAME);
|
||||
|
||||
builder.getstatic(lambdaClassName,
|
||||
LAMBDA_SINGLETON_FIELD_NAME,
|
||||
@@ -198,10 +221,7 @@ implements ClassVisitor,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: "+constantInstruction.toString(offset)+" -> new instance of "+lambdaClassName);
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: {} -> new instance of {}", constantInstruction.toString(offset), lambdaClassName);
|
||||
|
||||
int maxLocals = codeAttribute.u2maxLocals;
|
||||
|
||||
@@ -323,10 +343,7 @@ implements ClassVisitor,
|
||||
{
|
||||
String lambdaClassName = lambdaExpression.getLambdaClassName();
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: creating lambda class ["+lambdaClassName+"]");
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: creating lambda class [{}]", lambdaClassName);
|
||||
|
||||
// Start creating the lambda class.
|
||||
ClassBuilder classBuilder =
|
||||
@@ -439,10 +456,11 @@ implements ClassVisitor,
|
||||
int accessFlags =
|
||||
lambdaExpression.referencedInvokedMethod.getAccessFlags();
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: creating accessor method ["+className+"."+accessorMethodName+accessorMethodDescriptor+"]");
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: creating accessor method [{}.{}{}]",
|
||||
className,
|
||||
accessorMethodName,
|
||||
accessorMethodDescriptor
|
||||
);
|
||||
|
||||
// Method reference to a constructor.
|
||||
if (lambdaExpression.invokedReferenceKind == MethodHandleConstant.REF_NEW_INVOKE_SPECIAL)
|
||||
@@ -579,10 +597,11 @@ implements ClassVisitor,
|
||||
.areturn());
|
||||
}
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: creating interface method ["+lambdaClass.getName()+"."+lambdaExpression.interfaceMethod+lambdaExpression.interfaceMethodDescriptor+"]");
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: creating interface method [{}.{}{}]",
|
||||
lambdaClass.getName(),
|
||||
lambdaExpression.interfaceMethod,
|
||||
lambdaExpression.interfaceMethodDescriptor
|
||||
);
|
||||
|
||||
// Add the interface method.
|
||||
classBuilder.addMethod(
|
||||
@@ -637,10 +656,11 @@ implements ClassVisitor,
|
||||
// Add the constructor.
|
||||
String ctorDescriptor = lambdaExpression.getConstructorDescriptor();
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: creating constructor ["+lambdaClass+"."+ClassConstants.METHOD_NAME_INIT+ctorDescriptor+"]");
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: creating constructor [{}.{}{}]",
|
||||
lambdaClass,
|
||||
ClassConstants.METHOD_NAME_INIT,
|
||||
ctorDescriptor
|
||||
);
|
||||
|
||||
classBuilder.addMethod(
|
||||
AccessConstants.PUBLIC,
|
||||
@@ -684,10 +704,11 @@ implements ClassVisitor,
|
||||
String type = typeEnumeration.nextType();
|
||||
String fieldName = "arg$" + argIndex++;
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: creating field ["+lambdaClass+"."+fieldName+" "+type+"]");
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: creating field [{}.{} {}]",
|
||||
lambdaClass,
|
||||
fieldName,
|
||||
type
|
||||
);
|
||||
|
||||
classBuilder.addField(AccessConstants.PRIVATE |
|
||||
AccessConstants.FINAL,
|
||||
@@ -695,10 +716,11 @@ implements ClassVisitor,
|
||||
type);
|
||||
}
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: creating interface method [" + lambdaClassName + "." + lambdaExpression.interfaceMethod + lambdaExpression.interfaceMethodDescriptor + "]");
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: creating interface method [{}.{}{}]",
|
||||
lambdaClassName,
|
||||
lambdaExpression.interfaceMethod,
|
||||
lambdaExpression.interfaceMethodDescriptor
|
||||
);
|
||||
|
||||
// Add the interface method implementation.
|
||||
classBuilder.addMethod(
|
||||
@@ -869,10 +891,11 @@ implements ClassVisitor,
|
||||
Method method = lambdaClass.findMethod(methodName, bridgeMethodDescriptor);
|
||||
if (method == null)
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("LambdaExpressionConverter: adding bridge method ["+lambdaClass.getName()+"."+methodName+bridgeMethodDescriptor+"]");
|
||||
}
|
||||
logger.debug("LambdaExpressionConverter: adding bridge method [{}.{}{}]",
|
||||
lambdaClass.getName(),
|
||||
methodName,
|
||||
bridgeMethodDescriptor
|
||||
);
|
||||
|
||||
classBuilder.addMethod(
|
||||
AccessConstants.PUBLIC |
|
||||
80
base/src/main/java/proguard/classfile/ClassMemberPair.java
Normal file
80
base/src/main/java/proguard/classfile/ClassMemberPair.java
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package proguard.classfile;
|
||||
|
||||
import proguard.classfile.visitor.MemberVisitor;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Container class for a pair of class + member.
|
||||
*
|
||||
* @author James Hamilton
|
||||
*/
|
||||
public class ClassMemberPair
|
||||
{
|
||||
public final Clazz clazz;
|
||||
public final Member member;
|
||||
|
||||
|
||||
public ClassMemberPair(Clazz clazz, Member member)
|
||||
{
|
||||
this.clazz = clazz;
|
||||
this.member = member;
|
||||
}
|
||||
|
||||
|
||||
public void accept(MemberVisitor memberVisitor)
|
||||
{
|
||||
this.member.accept(this.clazz, memberVisitor);
|
||||
}
|
||||
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return this.member.getName(this.clazz);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ClassMemberPair that = (ClassMemberPair)o;
|
||||
return Objects.equals(clazz, that.clazz) &&
|
||||
Objects.equals(member, that.member);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(clazz, member);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return clazz.getName() + "." + this.member.getName(this.clazz) + this.member.getDescriptor(this.clazz);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*/
|
||||
|
||||
package proguard.classfile.pass;
|
||||
|
||||
import proguard.AppView;
|
||||
import proguard.classfile.util.ArrayInitializationReplacer;
|
||||
import proguard.pass.Pass;
|
||||
|
||||
/**
|
||||
* This pass replaces primitive array initialization code by primitive array constants.
|
||||
*/
|
||||
public class PrimitiveArrayConstantIntroducer implements Pass
|
||||
{
|
||||
@Override
|
||||
public void execute(AppView appView)
|
||||
{
|
||||
appView.programClassPool.classesAccept(new ArrayInitializationReplacer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package proguard.classfile.visitor;
|
||||
|
||||
import proguard.classfile.*;
|
||||
import proguard.util.ProcessingFlags;
|
||||
|
||||
|
||||
/**
|
||||
* This ClassVisitor delegates to one of two other visitors, depending on
|
||||
* whether the visited class was injected or not.
|
||||
*
|
||||
* @author Johan Leys
|
||||
*/
|
||||
public class InjectedClassFilter
|
||||
implements ClassVisitor
|
||||
{
|
||||
private final ClassVisitor injectedClassVisitor;
|
||||
private final ClassVisitor otherClassVisitor;
|
||||
|
||||
|
||||
public InjectedClassFilter(ClassVisitor injectedClassVisitor,
|
||||
ClassVisitor otherClassVisitor)
|
||||
{
|
||||
this.injectedClassVisitor = injectedClassVisitor;
|
||||
this.otherClassVisitor = otherClassVisitor;
|
||||
}
|
||||
|
||||
|
||||
// Implementations for ClassVisitor.
|
||||
|
||||
@Override
|
||||
public void visitAnyClass(Clazz clazz)
|
||||
{
|
||||
throw new UnsupportedOperationException(this.getClass().getName() + " does not support " + clazz.getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitProgramClass(ProgramClass programClass)
|
||||
{
|
||||
ClassVisitor delegate = delegateVisitor(programClass);
|
||||
if (delegate != null)
|
||||
{
|
||||
delegate.visitProgramClass(programClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visitLibraryClass(LibraryClass libraryClass)
|
||||
{
|
||||
if (otherClassVisitor != null)
|
||||
{
|
||||
otherClassVisitor.visitLibraryClass(libraryClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Small utility methods.
|
||||
|
||||
private ClassVisitor delegateVisitor(ProgramClass programClass)
|
||||
{
|
||||
return (programClass.processingFlags & ProcessingFlags.INJECTED) != 0 ?
|
||||
injectedClassVisitor : otherClassVisitor;
|
||||
}
|
||||
}
|
||||
1245
base/src/main/java/proguard/configuration/ConfigurationLogger.java
Normal file
1245
base/src/main/java/proguard/configuration/ConfigurationLogger.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package proguard.configuration;
|
||||
|
||||
|
||||
import proguard.AppView;
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.attribute.CodeAttribute;
|
||||
import proguard.classfile.attribute.visitor.AllAttributeVisitor;
|
||||
import proguard.classfile.editor.CodeAttributeEditor;
|
||||
import proguard.classfile.editor.PeepholeEditor;
|
||||
import proguard.classfile.instruction.Instruction;
|
||||
import proguard.classfile.instruction.visitor.InstructionVisitor;
|
||||
import proguard.classfile.util.BranchTargetFinder;
|
||||
import proguard.classfile.util.ClassReferenceInitializer;
|
||||
import proguard.classfile.util.ClassSubHierarchyInitializer;
|
||||
import proguard.classfile.visitor.AllMethodVisitor;
|
||||
import proguard.classfile.visitor.ClassPoolFiller;
|
||||
import proguard.classfile.visitor.ClassProcessingFlagFilter;
|
||||
import proguard.classfile.visitor.MultiClassVisitor;
|
||||
import proguard.io.ClassPathDataEntry;
|
||||
import proguard.io.ClassReader;
|
||||
import proguard.io.ExtraDataEntryNameMap;
|
||||
import proguard.pass.Pass;
|
||||
import proguard.util.ProcessingFlagSetter;
|
||||
import proguard.util.ProcessingFlags;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static proguard.configuration.ConfigurationLoggingInstructionSequenceConstants.*;
|
||||
|
||||
/**
|
||||
* This pass can add configuration debug logging code to all code that
|
||||
* relies on reflection. The added code prints suggestions on which keep
|
||||
* rules to add to ensure the reflection code will continue working after
|
||||
* obfuscation and shrinking.
|
||||
*
|
||||
* @author Johan Leys
|
||||
*/
|
||||
public class ConfigurationLoggingAdder implements Pass
|
||||
{
|
||||
/**
|
||||
* Instruments the given program class pool.
|
||||
*/
|
||||
@Override
|
||||
public void execute(AppView appView) throws IOException
|
||||
{
|
||||
// Load the logging utility classes in the program class pool.
|
||||
// TODO: The initialization could be incomplete if the loaded classes depend on one another.
|
||||
ClassReader classReader =
|
||||
new ClassReader(false, false, false, false, null,
|
||||
new MultiClassVisitor(
|
||||
new ClassPoolFiller(appView.programClassPool),
|
||||
new ClassReferenceInitializer(appView.programClassPool, appView.libraryClassPool),
|
||||
new ClassSubHierarchyInitializer(),
|
||||
new ProcessingFlagSetter(ProcessingFlags.INJECTED
|
||||
)));
|
||||
|
||||
classReader.read(new ClassPathDataEntry(ConfigurationLogger.ClassInfo.class));
|
||||
classReader.read(new ClassPathDataEntry(ConfigurationLogger.MemberInfo.class));
|
||||
classReader.read(new ClassPathDataEntry(ConfigurationLogger.class));
|
||||
|
||||
// Initialize the ConfigurationLogger class with the actual packageName.
|
||||
initializeConfigurationLogger(appView.programClassPool);
|
||||
|
||||
// Set up the instruction sequences and their replacements.
|
||||
ConfigurationLoggingInstructionSequenceConstants constants =
|
||||
new ConfigurationLoggingInstructionSequenceConstants(appView.programClassPool,
|
||||
appView.libraryClassPool);
|
||||
|
||||
BranchTargetFinder branchTargetFinder = new BranchTargetFinder();
|
||||
CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
|
||||
|
||||
// Replace the instruction sequences in all classes.
|
||||
// Do not add configuration debugging to any ProGuard runtime classes,
|
||||
// to avoid false positives.
|
||||
appView.programClassPool.classesAccept(
|
||||
new ClassProcessingFlagFilter(0, ProcessingFlags.INJECTED,
|
||||
new AllMethodVisitor(
|
||||
new AllAttributeVisitor(
|
||||
new PeepholeEditor(branchTargetFinder, codeAttributeEditor,
|
||||
new ConfigurationLoggingInstructionSequencesReplacer(constants.CONSTANTS,
|
||||
constants.RESOURCE,
|
||||
branchTargetFinder,
|
||||
codeAttributeEditor,
|
||||
new ExtraClassAdder(appView.extraDataEntryNameMap)))))));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialized the ConfigurationLogger class by injecting the actual packageName.
|
||||
*/
|
||||
private void initializeConfigurationLogger(ClassPool programClassPool)
|
||||
{
|
||||
ProgramClass configurationLoggerClass =
|
||||
(ProgramClass) programClassPool.getClass(LOGGER_CLASS_NAME);
|
||||
|
||||
if (configurationLoggerClass == null)
|
||||
{
|
||||
throw new RuntimeException("ConfigurationLogger class could not be found in the program classpool.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ExtraClassAdder
|
||||
implements InstructionVisitor
|
||||
{
|
||||
private final ExtraDataEntryNameMap extraDataEntryNameMap;
|
||||
|
||||
|
||||
ExtraClassAdder(ExtraDataEntryNameMap extraDataEntryNameMap)
|
||||
{
|
||||
this.extraDataEntryNameMap = extraDataEntryNameMap;
|
||||
}
|
||||
|
||||
|
||||
// Implementations for InstructionVisitor.
|
||||
|
||||
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
|
||||
{
|
||||
// Add a dependency from the modified class on the logging class.
|
||||
extraDataEntryNameMap.addExtraClassToClass(clazz, ConfigurationLogger.class);
|
||||
extraDataEntryNameMap.addExtraClassToClass(clazz, ConfigurationLogger.ClassInfo.class);
|
||||
extraDataEntryNameMap.addExtraClassToClass(clazz, ConfigurationLogger.MemberInfo.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package proguard.configuration;
|
||||
|
||||
import proguard.classfile.ClassPool;
|
||||
import proguard.classfile.constant.Constant;
|
||||
import proguard.classfile.editor.InstructionSequenceBuilder;
|
||||
import proguard.classfile.instruction.Instruction;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.classfile.util.InstructionSequenceMatcher;
|
||||
|
||||
|
||||
/**
|
||||
* This class contains a set of instruction sequences for accessing class
|
||||
* information via reflection, and replacement instructions that add logging
|
||||
* information on the reflection that is used.
|
||||
*
|
||||
* @author Johan Leys
|
||||
*/
|
||||
public class ConfigurationLoggingInstructionSequenceConstants
|
||||
{
|
||||
static final String LOGGER_CLASS_NAME = ClassUtil.internalClassName(ConfigurationLogger.class.getName());
|
||||
|
||||
|
||||
// Matched constants.
|
||||
public static final int CLASS_NAME = 0x30000000;
|
||||
public static final int LOCAL_VARIABLE_INDEX_1 = 0x30000001;
|
||||
public static final int LOCAL_VARIABLE_INDEX_2 = 0x30000002;
|
||||
public static final int LOCAL_VARIABLE_INDEX_3 = 0x30000003;
|
||||
|
||||
public static final int CONSTANT_INDEX = InstructionSequenceMatcher.X;
|
||||
|
||||
public final Instruction[][][] RESOURCE;
|
||||
public final Constant[] CONSTANTS;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of ResourceIdInstructionSequenceConstants,
|
||||
* with constants that reference classes from the given class pools.
|
||||
*/
|
||||
public ConfigurationLoggingInstructionSequenceConstants(ClassPool programClassPool,
|
||||
ClassPool libraryClassPool)
|
||||
{
|
||||
InstructionSequenceBuilder ____ =
|
||||
new InstructionSequenceBuilder(programClassPool, libraryClassPool);
|
||||
|
||||
RESOURCE = new Instruction[][][]
|
||||
{
|
||||
// Classes.
|
||||
{
|
||||
// Automatically detected and kept - don't check anything.
|
||||
____.ldc_(CONSTANT_INDEX)
|
||||
.invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;").__(),
|
||||
|
||||
____.ldc_(CONSTANT_INDEX)
|
||||
.invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;").__()
|
||||
},
|
||||
{
|
||||
____.invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;").__(),
|
||||
|
||||
____.dup()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkForName", "(Ljava/lang/String;Ljava/lang/String;)V")
|
||||
.invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;").__()
|
||||
},
|
||||
{
|
||||
____.invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;").__(),
|
||||
|
||||
____.dup_x2()
|
||||
.pop()
|
||||
.dup_x2()
|
||||
.pop()
|
||||
.dup_x2()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkForName", "(Ljava/lang/String;Ljava/lang/String;)V")
|
||||
.invokestatic("java/lang/Class", "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;").__(),
|
||||
|
||||
____.dup()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkLoadClass", "(Ljava/lang/String;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;").__(),
|
||||
},
|
||||
|
||||
// Constructors.
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;").__(),
|
||||
|
||||
____.dup2()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetDeclaredConstructor", "(Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;").__(),
|
||||
|
||||
____.dup2()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetConstructor", "(Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;").__(),
|
||||
|
||||
____.dup()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetDeclaredConstructors", "(Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getConstructors", "()[Ljava/lang/reflect/Constructor;").__(),
|
||||
|
||||
____.dup()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetConstructors", "(Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getConstructors", "()[Ljava/lang/reflect/Constructor;").__()
|
||||
},
|
||||
|
||||
// Methods.
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;").__(),
|
||||
|
||||
____.dup_x2()
|
||||
.astore(LOCAL_VARIABLE_INDEX_1)
|
||||
.dup_x2()
|
||||
.astore(LOCAL_VARIABLE_INDEX_2)
|
||||
.dup_x2()
|
||||
.astore(LOCAL_VARIABLE_INDEX_3)
|
||||
.aload(LOCAL_VARIABLE_INDEX_3)
|
||||
.aload(LOCAL_VARIABLE_INDEX_2)
|
||||
.aload(LOCAL_VARIABLE_INDEX_1)
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetDeclaredMethod", "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;").__(),
|
||||
|
||||
____.dup_x2()
|
||||
.astore(LOCAL_VARIABLE_INDEX_1)
|
||||
.dup_x2()
|
||||
.astore(LOCAL_VARIABLE_INDEX_2)
|
||||
.dup_x2()
|
||||
.astore(LOCAL_VARIABLE_INDEX_3)
|
||||
.aload(LOCAL_VARIABLE_INDEX_3)
|
||||
.aload(LOCAL_VARIABLE_INDEX_2)
|
||||
.aload(LOCAL_VARIABLE_INDEX_1)
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetMethod", "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;").__(),
|
||||
|
||||
____.dup()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetDeclaredMethods", "(Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;").__(),
|
||||
|
||||
____.dup()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetMethods", "(Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;").__()
|
||||
},
|
||||
|
||||
// Fields.
|
||||
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;").__(),
|
||||
|
||||
____.dup2()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetDeclaredField", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;").__(),
|
||||
|
||||
____.dup2()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetField", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getDeclaredFields", "()[Ljava/lang/reflect/Field;").__(),
|
||||
|
||||
____.dup()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetDeclaredFields", "(Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getDeclaredFields", "()[Ljava/lang/reflect/Field;").__()
|
||||
},
|
||||
{
|
||||
____.invokevirtual("java/lang/Class", "getFields", "()[Ljava/lang/reflect/Field;").__(),
|
||||
|
||||
____.dup()
|
||||
.ldc_(CLASS_NAME)
|
||||
.invokestatic(LOGGER_CLASS_NAME, "checkGetFields", "(Ljava/lang/Class;Ljava/lang/String;)V")
|
||||
.invokevirtual("java/lang/Class", "getFields", "()[Ljava/lang/reflect/Field;").__()
|
||||
},
|
||||
};
|
||||
|
||||
CONSTANTS = ____.constants();
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -18,21 +18,25 @@
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
package proguard.configuration;
|
||||
|
||||
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.Clazz;
|
||||
import proguard.classfile.Method;
|
||||
import proguard.classfile.ProgramClass;
|
||||
import proguard.classfile.attribute.CodeAttribute;
|
||||
import proguard.classfile.constant.Constant;
|
||||
import proguard.classfile.editor.*;
|
||||
import proguard.classfile.editor.CodeAttributeEditor;
|
||||
import proguard.classfile.editor.ConstantPoolEditor;
|
||||
import proguard.classfile.editor.InstructionSequenceReplacer;
|
||||
import proguard.classfile.instruction.Instruction;
|
||||
import proguard.classfile.instruction.visitor.InstructionVisitor;
|
||||
import proguard.classfile.util.*;
|
||||
import proguard.optimize.peephole.*;
|
||||
import proguard.classfile.util.BranchTargetFinder;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.classfile.util.InstructionSequenceMatcher;
|
||||
|
||||
import static proguard.configuration.ConfigurationLoggingInstructionSequenceConstants.LOCAL_VARIABLE_INDEX_1;
|
||||
import static proguard.configuration.ConfigurationLoggingInstructionSequenceConstants.LOCAL_VARIABLE_INDEX_2;
|
||||
import static proguard.configuration.ConfigurationLoggingInstructionSequenceConstants.LOCAL_VARIABLE_INDEX_3;
|
||||
import static proguard.configuration.ConfigurationLoggingInstructionSequenceConstants.*;
|
||||
|
||||
/**
|
||||
* This InstructionSequencesReplacer appends logging instructions to all
|
||||
@@ -2,7 +2,7 @@
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2020 Guardsquare NV
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@@ -21,9 +21,12 @@
|
||||
package proguard.configuration;
|
||||
|
||||
import proguard.classfile.constant.Constant;
|
||||
import proguard.classfile.editor.*;
|
||||
import proguard.classfile.editor.CodeAttributeEditor;
|
||||
import proguard.classfile.editor.InstructionSequenceReplacer;
|
||||
import proguard.classfile.editor.InstructionSequencesReplacer;
|
||||
import proguard.classfile.instruction.Instruction;
|
||||
import proguard.classfile.instruction.visitor.*;
|
||||
import proguard.classfile.instruction.visitor.InstructionVisitor;
|
||||
import proguard.classfile.instruction.visitor.MultiInstructionVisitor;
|
||||
import proguard.classfile.util.BranchTargetFinder;
|
||||
|
||||
/**
|
||||
151
base/src/main/java/proguard/configuration/InitialStateInfo.java
Normal file
151
base/src/main/java/proguard/configuration/InitialStateInfo.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package proguard.configuration;
|
||||
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.resources.file.ResourceFilePool;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static proguard.util.HashUtil.hashFnv1a32_UTF8;
|
||||
|
||||
/**
|
||||
* Stores the initial state of a classpool and resource files including
|
||||
* class names, super class names and hashes of fields and methods; and
|
||||
* resource filenames.
|
||||
*
|
||||
* @author James Hamilton
|
||||
*/
|
||||
public class InitialStateInfo
|
||||
{
|
||||
private final List<String> classNames = new ArrayList<>();
|
||||
private final Map<String, String> superClassNames = new HashMap<>();
|
||||
private final Map<String, Map<ProgramMethod, Integer>> methodHashes = new HashMap<>();
|
||||
private final Map<String, Map<ProgramField, Integer>> fieldHashes = new HashMap<>();
|
||||
|
||||
|
||||
public InitialStateInfo(ClassPool classPool)
|
||||
{
|
||||
this.initialize(classPool);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return The size of the class pool
|
||||
*/
|
||||
public int size()
|
||||
{
|
||||
return this.classNames.size();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return The class names
|
||||
*/
|
||||
public List<String> classNames()
|
||||
{
|
||||
return classNames;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a class name return it's super class name.
|
||||
*
|
||||
* @param className a class name
|
||||
*
|
||||
* @return super class name
|
||||
*/
|
||||
public String getSuperClassName(String className)
|
||||
{
|
||||
return this.superClassNames.get(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a class name return a mapping of method -> original method hash
|
||||
*
|
||||
* @param className a class name
|
||||
*
|
||||
* @return map ProgramMethod -> hash(original method signature)
|
||||
*/
|
||||
public Map<ProgramMethod, Integer> getMethodHashMap(String className)
|
||||
{
|
||||
return this.methodHashes.containsKey(className) ? this.methodHashes.get(className) : new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a class name return a mapping of a field -> original field hash
|
||||
*
|
||||
* @param className a class name
|
||||
*
|
||||
* @return map ProgramField -> hash(original field name)
|
||||
*/
|
||||
public Map<ProgramField, Integer> getFieldHashMap(String className)
|
||||
{
|
||||
return this.fieldHashes.containsKey(className) ? this.fieldHashes.get(className) : new HashMap<>();
|
||||
}
|
||||
|
||||
|
||||
// Private utility methods.
|
||||
|
||||
private void initialize(ClassPool classPool)
|
||||
{
|
||||
Iterator<String> iterator = classPool.classNames();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
String className = iterator.next();
|
||||
classNames.add(className);
|
||||
ProgramClass programClass = (ProgramClass)classPool.getClass(className);
|
||||
superClassNames.put(className, programClass.getSuperName());
|
||||
|
||||
for (ProgramMethod programMethod : programClass.methods)
|
||||
{
|
||||
if (!methodHashes.containsKey(className))
|
||||
{
|
||||
methodHashes.put(className, new HashMap<>());
|
||||
}
|
||||
methodHashes.get(className).put(programMethod, hash(programClass, programMethod));
|
||||
}
|
||||
|
||||
for (ProgramField programField : programClass.fields)
|
||||
{
|
||||
if (!fieldHashes.containsKey(className))
|
||||
{
|
||||
fieldHashes.put(className, new HashMap<>());
|
||||
}
|
||||
fieldHashes.get(className).put(programField, hash(programClass, programField));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static int hash(Clazz clazz, Method method)
|
||||
{
|
||||
return hashFnv1a32_UTF8(method.getName(clazz) + "(" + ClassUtil.externalMethodArguments(method.getDescriptor(clazz)) + ")") ;
|
||||
}
|
||||
|
||||
|
||||
private static int hash(Clazz clazz, Field field)
|
||||
{
|
||||
return hashFnv1a32_UTF8(field.getName(clazz));
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ implements MemberVisitor,
|
||||
public int getParameterAnnotationCount(int index)
|
||||
{
|
||||
return parameterAnnotationCount != null &&
|
||||
parameterAnnotationCount.length > 0 && index <= parameterAnnotationCount.length
|
||||
parameterAnnotationCount.length > 0 && index < parameterAnnotationCount.length
|
||||
? parameterAnnotationCount[index]
|
||||
: -1;
|
||||
}
|
||||
@@ -64,13 +64,15 @@ implements KotlinMetadataVisitor,
|
||||
{
|
||||
visitKotlinDeclarationContainerMetadata(clazz, kotlinClassKindMetadata);
|
||||
|
||||
kotlinClassKindMetadata.superTypesAccept( clazz, this);
|
||||
kotlinClassKindMetadata.typeParametersAccept( clazz, this);
|
||||
kotlinClassKindMetadata.versionRequirementAccept(clazz, this);
|
||||
kotlinClassKindMetadata.constructorsAccept( clazz, this);
|
||||
kotlinClassKindMetadata.contextReceiverTypesAccept( clazz, this);
|
||||
kotlinClassKindMetadata.superTypesAccept( clazz, this);
|
||||
kotlinClassKindMetadata.typeParametersAccept( clazz, this);
|
||||
kotlinClassKindMetadata.versionRequirementAccept( clazz, this);
|
||||
kotlinClassKindMetadata.constructorsAccept( clazz, this);
|
||||
kotlinClassKindMetadata.inlineClassUnderlyingPropertyTypeAccept(clazz, this);
|
||||
|
||||
kotlinClassKindMetadata.referencedClass.attributesAccept(annotationCounter.reset());
|
||||
kotlinClassKindMetadata.flags.common.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
kotlinClassKindMetadata.flags.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,11 +105,12 @@ implements KotlinMetadataVisitor,
|
||||
KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata,
|
||||
KotlinPropertyMetadata kotlinPropertyMetadata)
|
||||
{
|
||||
kotlinPropertyMetadata.versionRequirementAccept(clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.typeAccept( clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.setterParametersAccept( clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.receiverTypeAccept( clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.typeParametersAccept( clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.versionRequirementAccept(clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.typeAccept( clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.setterParametersAccept( clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.contextReceiverTypesAccept(clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.receiverTypeAccept( clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinPropertyMetadata.typeParametersAccept( clazz, kotlinDeclarationContainerMetadata, this);
|
||||
|
||||
if (kotlinPropertyMetadata.syntheticMethodForAnnotations != null)
|
||||
{
|
||||
@@ -116,28 +119,28 @@ implements KotlinMetadataVisitor,
|
||||
annotationCounter.reset()
|
||||
);
|
||||
|
||||
kotlinPropertyMetadata.flags.common.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
kotlinPropertyMetadata.flags.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
}
|
||||
else if (kotlinPropertyMetadata.referencedBackingField != null)
|
||||
{
|
||||
kotlinPropertyMetadata.referencedBackingField.accept(kotlinPropertyMetadata.referencedBackingFieldClass, annotationCounter);
|
||||
kotlinPropertyMetadata.flags.common.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
kotlinPropertyMetadata.flags.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
kotlinPropertyMetadata.flags.common.hasAnnotations = false;
|
||||
kotlinPropertyMetadata.flags.hasAnnotations = false;
|
||||
}
|
||||
|
||||
if (kotlinPropertyMetadata.flags.hasGetter && kotlinPropertyMetadata.referencedGetterMethod != null)
|
||||
if (kotlinPropertyMetadata.referencedGetterMethod != null)
|
||||
{
|
||||
kotlinPropertyMetadata.referencedGetterMethod.accept(clazz, annotationCounter.reset());
|
||||
kotlinPropertyMetadata.getterFlags.common.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
kotlinPropertyMetadata.getterFlags.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
}
|
||||
|
||||
if (kotlinPropertyMetadata.flags.hasSetter && kotlinPropertyMetadata.referencedSetterMethod != null)
|
||||
if (kotlinPropertyMetadata.flags.isVar && kotlinPropertyMetadata.referencedSetterMethod != null)
|
||||
{
|
||||
kotlinPropertyMetadata.referencedSetterMethod.accept(clazz, annotationCounter.reset());
|
||||
kotlinPropertyMetadata.setterFlags.common.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
kotlinPropertyMetadata.setterFlags.hasAnnotations = annotationCounter.getCount() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,13 +150,14 @@ implements KotlinMetadataVisitor,
|
||||
KotlinMetadata kotlinMetadata,
|
||||
KotlinFunctionMetadata kotlinFunctionMetadata)
|
||||
{
|
||||
kotlinFunctionMetadata.receiverTypeAccept( clazz, kotlinMetadata, this);
|
||||
kotlinFunctionMetadata.typeParametersAccept( clazz, kotlinMetadata, this);
|
||||
kotlinFunctionMetadata.valueParametersAccept(clazz, kotlinMetadata, this);
|
||||
kotlinFunctionMetadata.returnTypeAccept( clazz, kotlinMetadata, this);
|
||||
kotlinFunctionMetadata.contextReceiverTypesAccept(clazz, kotlinMetadata, this);
|
||||
kotlinFunctionMetadata.receiverTypeAccept( clazz, kotlinMetadata, this);
|
||||
kotlinFunctionMetadata.typeParametersAccept( clazz, kotlinMetadata, this);
|
||||
kotlinFunctionMetadata.valueParametersAccept( clazz, kotlinMetadata, this);
|
||||
kotlinFunctionMetadata.returnTypeAccept( clazz, kotlinMetadata, this);
|
||||
|
||||
kotlinFunctionMetadata.referencedMethod.accept(kotlinFunctionMetadata.referencedMethodClass, annotationCounter.reset());
|
||||
kotlinFunctionMetadata.flags.common.hasAnnotations = annotationCounter.getCount() != 0;
|
||||
kotlinFunctionMetadata.referencedMethodAccept(annotationCounter.reset());
|
||||
kotlinFunctionMetadata.flags.hasAnnotations = annotationCounter.getCount() != 0;
|
||||
}
|
||||
|
||||
// Implementations for KotlinConstructorVisitor.
|
||||
@@ -168,12 +172,12 @@ implements KotlinMetadataVisitor,
|
||||
if (kotlinClassKindMetadata.flags.isAnnotationClass)
|
||||
{
|
||||
//PROBBUG where are the annotations?
|
||||
kotlinConstructorMetadata.flags.common.hasAnnotations = false;
|
||||
kotlinConstructorMetadata.flags.hasAnnotations = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
kotlinConstructorMetadata.referencedMethod.accept(clazz, annotationCounter.reset());
|
||||
kotlinConstructorMetadata.flags.common.hasAnnotations = annotationCounter.getCount() != 0;
|
||||
kotlinConstructorMetadata.referencedMethodAccept(clazz, annotationCounter.reset());
|
||||
kotlinConstructorMetadata.flags.hasAnnotations = annotationCounter.getCount() != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +192,7 @@ implements KotlinMetadataVisitor,
|
||||
kotlinTypeAliasMetadata.expandedTypeAccept( clazz, kotlinDeclarationContainerMetadata, this);
|
||||
kotlinTypeAliasMetadata.versionRequirementAccept(clazz, kotlinDeclarationContainerMetadata, this);
|
||||
|
||||
kotlinTypeAliasMetadata.flags.common.hasAnnotations = !kotlinTypeAliasMetadata.annotations.isEmpty();
|
||||
kotlinTypeAliasMetadata.flags.hasAnnotations = !kotlinTypeAliasMetadata.annotations.isEmpty();
|
||||
}
|
||||
|
||||
// Implementations for KotlinTypeVisitor.
|
||||
@@ -198,8 +202,6 @@ implements KotlinMetadataVisitor,
|
||||
kotlinTypeMetadata.typeArgumentsAccept(clazz, this);
|
||||
kotlinTypeMetadata.upperBoundsAccept( clazz, this);
|
||||
kotlinTypeMetadata.abbreviationAccept( clazz, this);
|
||||
|
||||
kotlinTypeMetadata.flags.common.hasAnnotations = !kotlinTypeMetadata.annotations.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -208,8 +210,7 @@ implements KotlinMetadataVisitor,
|
||||
KotlinFunctionMetadata kotlinFunctionMetadata,
|
||||
KotlinTypeMetadata kotlinTypeMetadata)
|
||||
{
|
||||
kotlinFunctionMetadata.referencedMethod.accept(kotlinFunctionMetadata.referencedMethodClass, this.annotationCounter.reset());
|
||||
kotlinTypeMetadata.flags.common.hasAnnotations = annotationCounter.getParameterAnnotationCount(0) > 0;
|
||||
kotlinFunctionMetadata.referencedMethodAccept(this.annotationCounter.reset());
|
||||
}
|
||||
|
||||
// Implementations for KotlinTypeParameterVisitor.
|
||||
@@ -221,8 +222,6 @@ implements KotlinMetadataVisitor,
|
||||
public void visitAnyTypeParameter(Clazz clazz, KotlinTypeParameterMetadata kotlinTypeParameterMetadata)
|
||||
{
|
||||
kotlinTypeParameterMetadata.upperBoundsAccept(clazz, this);
|
||||
|
||||
kotlinTypeParameterMetadata.flags.common.hasAnnotations = !kotlinTypeParameterMetadata.annotations.isEmpty();
|
||||
}
|
||||
|
||||
// Implementations for KotlinValueParameterVisitor.
|
||||
@@ -238,10 +237,10 @@ implements KotlinMetadataVisitor,
|
||||
kotlinFunctionMetadata,
|
||||
this);
|
||||
|
||||
if (kotlinValueParameterMetadata.flags.common.hasAnnotations)
|
||||
if (kotlinValueParameterMetadata.flags.hasAnnotations)
|
||||
{
|
||||
kotlinFunctionMetadata.referencedMethod.accept(kotlinFunctionMetadata.referencedMethodClass, annotationCounter.reset());
|
||||
kotlinValueParameterMetadata.flags.common.hasAnnotations =
|
||||
kotlinFunctionMetadata.referencedMethodAccept(annotationCounter.reset());
|
||||
kotlinValueParameterMetadata.flags.hasAnnotations =
|
||||
annotationCounter.getParameterAnnotationCount(kotlinValueParameterMetadata.index) > 0;
|
||||
}
|
||||
}
|
||||
@@ -257,12 +256,12 @@ implements KotlinMetadataVisitor,
|
||||
kotlinConstructorMetadata,
|
||||
this);
|
||||
|
||||
if (kotlinValueParameterMetadata.flags.common.hasAnnotations)
|
||||
if (kotlinValueParameterMetadata.flags.hasAnnotations)
|
||||
{
|
||||
if (!kotlinClassKindMetadata.flags.isAnnotationClass)
|
||||
{
|
||||
kotlinConstructorMetadata.referencedMethod.accept(clazz, annotationCounter.reset());
|
||||
kotlinValueParameterMetadata.flags.common.hasAnnotations =
|
||||
kotlinConstructorMetadata.referencedMethodAccept(clazz, annotationCounter.reset());
|
||||
kotlinValueParameterMetadata.flags.hasAnnotations =
|
||||
annotationCounter.getParameterAnnotationCount(kotlinValueParameterMetadata.index) > 0;
|
||||
}
|
||||
}
|
||||
@@ -279,10 +278,10 @@ implements KotlinMetadataVisitor,
|
||||
kotlinPropertyMetadata,
|
||||
this);
|
||||
|
||||
if (kotlinValueParameterMetadata.flags.common.hasAnnotations)
|
||||
if (kotlinValueParameterMetadata.flags.hasAnnotations)
|
||||
{
|
||||
kotlinPropertyMetadata.referencedSetterMethod.accept(clazz, annotationCounter.reset());
|
||||
kotlinValueParameterMetadata.flags.common.hasAnnotations =
|
||||
kotlinValueParameterMetadata.flags.hasAnnotations =
|
||||
annotationCounter.getParameterAnnotationCount(kotlinValueParameterMetadata.index) > 0;
|
||||
}
|
||||
}
|
||||
297
base/src/main/java/proguard/io/ClassMapDataEntryReplacer.java
Normal file
297
base/src/main/java/proguard/io/ClassMapDataEntryReplacer.java
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
||||
* of Java bytecode.
|
||||
*
|
||||
* Copyright (c) 2002-2021 Guardsquare NV
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package proguard.io;
|
||||
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.util.ClassUtil;
|
||||
import proguard.classfile.visitor.*;
|
||||
import proguard.configuration.InitialStateInfo;
|
||||
import proguard.util.ProcessingFlags;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static proguard.configuration.ConfigurationLogger.*;
|
||||
|
||||
|
||||
/**
|
||||
* This DataEntryReader writes a class mapping to each received data
|
||||
* entry, used for debugging of the configuration.
|
||||
*
|
||||
* @author Johan Leys
|
||||
*/
|
||||
public class ClassMapDataEntryReplacer
|
||||
implements DataEntryReader
|
||||
{
|
||||
private final ClassPool programClassPool;
|
||||
private final InitialStateInfo initialStateInfo;
|
||||
private final DataEntryWriter dataEntryWriter;
|
||||
private DataOutputStream dataOutputStream;
|
||||
|
||||
public ClassMapDataEntryReplacer(ClassPool programClassPool,
|
||||
InitialStateInfo initialStateInfo,
|
||||
DataEntryWriter dataEntryWriter)
|
||||
{
|
||||
this.programClassPool = programClassPool;
|
||||
this.initialStateInfo = initialStateInfo;
|
||||
this.dataEntryWriter = dataEntryWriter;
|
||||
}
|
||||
|
||||
// Implementations for DataEntryReader.
|
||||
|
||||
@Override
|
||||
public void read(DataEntry dataEntry) throws IOException
|
||||
{
|
||||
OutputStream outputStream = dataEntryWriter.createOutputStream(dataEntry);
|
||||
if (outputStream != null)
|
||||
{
|
||||
dataOutputStream = new DataOutputStream(outputStream);
|
||||
try
|
||||
{
|
||||
dataOutputStream.writeInt(initialStateInfo.size());
|
||||
writeClassMap();
|
||||
}
|
||||
finally
|
||||
{
|
||||
dataOutputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Private utility methods.
|
||||
|
||||
private void writeClassMap() throws IOException
|
||||
{
|
||||
for (String className : initialStateInfo.classNames())
|
||||
{
|
||||
ProgramClass clazz = (ProgramClass)programClassPool.getClass(className);
|
||||
|
||||
dataOutputStream.writeUTF(ClassUtil.externalClassName(className));
|
||||
|
||||
if (clazz == null)
|
||||
{
|
||||
// The class is no longer in the classpool, so it must have been shrunk.
|
||||
// obfuscated name (original name, because it was removed from class pool so not obfuscated)
|
||||
dataOutputStream.writeUTF(ClassUtil.externalClassName(className));
|
||||
dataOutputStream.writeUTF(ClassUtil.externalClassName(initialStateInfo.getSuperClassName(className)));
|
||||
// flag
|
||||
dataOutputStream.writeShort(CLASS_SHRUNK);
|
||||
writeMembers(initialStateInfo.getFieldHashMap(className), Collections.emptyList());
|
||||
writeMembers(initialStateInfo.getMethodHashMap(className), Collections.emptyList());
|
||||
}
|
||||
else
|
||||
{
|
||||
// obfuscated name
|
||||
dataOutputStream.writeUTF(ClassUtil.externalClassName(clazz.getName()));
|
||||
dataOutputStream.writeUTF(ClassUtil.externalClassName(clazz.getSuperName()));
|
||||
dataOutputStream.writeShort(
|
||||
(isKept(clazz.processingFlags) ? CLASS_KEPT : 0) |
|
||||
(allDeclaredFieldsKept(clazz) ? ALL_DECLARED_FIELDS_KEPT : 0) |
|
||||
(allPublicFieldsKept(clazz) ? ALL_PUBLIC_FIELDS_KEPT : 0) |
|
||||
(allDeclaredConstructorsKept(clazz) ? ALL_DECLARED_CONSTRUCTORS_KEPT : 0) |
|
||||
(allPublicConstructorsKept(clazz) ? ALL_PUBLIC_CONSTRUCTORS_KEPT : 0) |
|
||||
(allDeclaredMethodsKept(clazz) ? ALL_DECLARED_METHODS_KEPT : 0) |
|
||||
(allPublicMethodsKept(clazz) ? ALL_PUBLIC_METHODS_KEPT : 0)
|
||||
);
|
||||
writeMembers(initialStateInfo.getFieldHashMap(className), Arrays.asList(clazz.fields));
|
||||
writeMembers(initialStateInfo.getMethodHashMap(className), Arrays.asList(clazz.methods));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Member> void writeMembers(Map<T, Integer> originalMemberHashes, Collection<T> currentMembers)
|
||||
throws IOException
|
||||
{
|
||||
dataOutputStream.writeShort(originalMemberHashes.size());
|
||||
for (Map.Entry<T, Integer> entry : originalMemberHashes.entrySet())
|
||||
{
|
||||
dataOutputStream.writeInt(entry.getValue());
|
||||
dataOutputStream.writeByte((currentMembers.contains(entry.getKey()) ? 0 : MEMBER_SHRUNK) |
|
||||
(isKept(entry.getKey().getProcessingFlags()) ? MEMBER_KEPT : 0));
|
||||
}
|
||||
}
|
||||
|
||||
// Small utility methods.
|
||||
|
||||
static boolean isKept(int processingFlags)
|
||||
{
|
||||
return (processingFlags & ProcessingFlags.DONT_OBFUSCATE) != 0 &&
|
||||
(processingFlags & ProcessingFlags.DONT_SHRINK) != 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether all fields of the class (not including the fields of super classes) are kept.
|
||||
*/
|
||||
private static boolean allDeclaredFieldsKept(Clazz clazz)
|
||||
{
|
||||
if ((clazz.getProcessingFlags() & ProcessingFlags.REMOVED_FIELDS) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MemberCounter unkeptCounter = new MemberCounter();
|
||||
|
||||
clazz.fieldsAccept(
|
||||
new MemberProcessingFlagFilter(0, ProcessingFlags.INJECTED,
|
||||
new MemberProcessingFlagFilter(ProcessingFlags.DONT_SHRINK | ProcessingFlags.DONT_OBFUSCATE, 0,
|
||||
null,
|
||||
unkeptCounter)));
|
||||
|
||||
return unkeptCounter.getCount() == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether all public fields of the class, including the public fields of super classes, are kept.
|
||||
*/
|
||||
private static boolean allPublicFieldsKept(Clazz clazz)
|
||||
{
|
||||
ClassCounter removedCounter = new ClassCounter();
|
||||
MemberCounter unkeptCounter = new MemberCounter();
|
||||
|
||||
clazz.hierarchyAccept(true, true, false, false,
|
||||
new ProgramClassFilter(
|
||||
new MultiClassVisitor(
|
||||
// Check for removed public fields.
|
||||
new ClassProcessingFlagFilter(ProcessingFlags.REMOVED_PUBLIC_FIELDS, 0,
|
||||
removedCounter),
|
||||
// Check for unkept public fields.
|
||||
new AllFieldVisitor(
|
||||
new MemberAccessFilter(AccessConstants.PUBLIC, 0,
|
||||
new MemberProcessingFlagFilter(0, ProcessingFlags.INJECTED,
|
||||
new MemberProcessingFlagFilter(ProcessingFlags.DONT_SHRINK | ProcessingFlags.DONT_OBFUSCATE, 0,
|
||||
null,
|
||||
unkeptCounter))))
|
||||
)));
|
||||
|
||||
return removedCounter.getCount() == 0 && unkeptCounter.getCount() == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether all constructors of the class (not including the constructors of super classes) are kept.
|
||||
*/
|
||||
private static boolean allDeclaredConstructorsKept(Clazz clazz)
|
||||
{
|
||||
if ((clazz.getProcessingFlags() & ProcessingFlags.REMOVED_CONSTRUCTORS) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MemberCounter unkeptCounter = new MemberCounter();
|
||||
|
||||
clazz.methodsAccept(
|
||||
new ConstructorMethodFilter(
|
||||
new MemberProcessingFlagFilter(0, ProcessingFlags.INJECTED,
|
||||
new MemberProcessingFlagFilter(ProcessingFlags.DONT_SHRINK | ProcessingFlags.DONT_OBFUSCATE, 0,
|
||||
null,
|
||||
unkeptCounter))));
|
||||
|
||||
return unkeptCounter.getCount() == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether all public constructors of the class (not including the public constructors of super classes)
|
||||
* are kept.
|
||||
*/
|
||||
private static boolean allPublicConstructorsKept(Clazz clazz)
|
||||
{
|
||||
if ((clazz.getProcessingFlags() & ProcessingFlags.REMOVED_PUBLIC_CONSTRUCTORS) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MemberCounter unkeptCounter = new MemberCounter();
|
||||
|
||||
clazz.hierarchyAccept(true, true, false, false,
|
||||
new ProgramClassFilter(
|
||||
new AllMethodVisitor(
|
||||
new ConstructorMethodFilter(
|
||||
new MemberAccessFilter(AccessConstants.PUBLIC, 0,
|
||||
new MemberProcessingFlagFilter(0, ProcessingFlags.INJECTED,
|
||||
new MemberProcessingFlagFilter(ProcessingFlags.DONT_SHRINK | ProcessingFlags.DONT_OBFUSCATE, 0,
|
||||
null,
|
||||
unkeptCounter)))))));
|
||||
|
||||
return unkeptCounter.getCount() == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether all methods of the class (not including the methods of super classes) are kept.
|
||||
*/
|
||||
private static boolean allDeclaredMethodsKept(Clazz clazz)
|
||||
{
|
||||
if ((clazz.getProcessingFlags() & ProcessingFlags.REMOVED_METHODS) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MemberCounter unkeptCounter = new MemberCounter();
|
||||
|
||||
clazz.methodsAccept(
|
||||
new InitializerMethodFilter(
|
||||
null,
|
||||
new MemberProcessingFlagFilter(0, ProcessingFlags.INJECTED,
|
||||
new MemberProcessingFlagFilter(ProcessingFlags.DONT_SHRINK | ProcessingFlags.DONT_OBFUSCATE, 0,
|
||||
null,
|
||||
unkeptCounter))));
|
||||
|
||||
return unkeptCounter.getCount() == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether all public methods of the class, including the public methods of super classes, are kept.
|
||||
*/
|
||||
private static boolean allPublicMethodsKept(Clazz clazz)
|
||||
{
|
||||
ClassCounter removedCounter = new ClassCounter();
|
||||
MemberCounter unkeptCounter = new MemberCounter();
|
||||
|
||||
clazz.hierarchyAccept(true, true, false, false,
|
||||
new ProgramClassFilter(
|
||||
new MultiClassVisitor(
|
||||
// Check for removed public methods.
|
||||
new ClassProcessingFlagFilter(ProcessingFlags.REMOVED_PUBLIC_METHODS, 0,
|
||||
removedCounter),
|
||||
// Check for unkept public methods.
|
||||
new AllMethodVisitor(
|
||||
new InitializerMethodFilter(
|
||||
null,
|
||||
new MemberAccessFilter(AccessConstants.PUBLIC, 0,
|
||||
new MemberProcessingFlagFilter(0, ProcessingFlags.INJECTED,
|
||||
new MemberProcessingFlagFilter(ProcessingFlags.DONT_SHRINK | ProcessingFlags.DONT_OBFUSCATE, 0,
|
||||
null,
|
||||
unkeptCounter)))))
|
||||
)));
|
||||
|
||||
return removedCounter.getCount() == 0 && unkeptCounter.getCount() == 0;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user