Compare commits

..

433 Commits

Author SHA1 Message Date
niccolo.piazzesi
869ce156b1 Update dependency, update samples, update release notes for 7.8 2025-10-07 13:34:28 +02:00
Fergal Whyte
124b33e473 Bump ProGuard to 7.8.0 and ProGuardCORE to 9.2.0 2025-10-07 13:34:16 +02:00
Robin Lefever
b8a62c8ca8 Bump Nexus publish plugin version 2025-08-25 11:53:54 +02:00
niccolo.piazzesi
886477806c Temporarily downgrade version in examples until release is properly done 2025-08-22 10:47:01 +02:00
Robin Lefever
7fc907d1fb Update ProGuardCORE version to support Kotlin 2.2 2025-08-22 06:48:58 +02:00
piazzesiNiccolo-GS
b10346ba32 Upgrade and cleanup ci configuration (#492) 2025-08-21 09:57:43 +02:00
Thomas Vochten
f2ced20be4 Fix ktlint issue in ClassUsageMarkerTest 2025-08-21 08:28:19 +02:00
huqiuser
35ea6d587f fix NullPointerException for Kotlin typealias of typealias of lambda (#490) 2025-08-20 16:25:40 +02:00
Eric Salemi
eea0ccbe8f Migrate to OSSRH staging API service 2025-08-20 08:18:34 +02:00
Joren Hannes
bee74a9963 Run kotlin linter 2025-08-13 14:56:39 +02:00
Oberon Swings
4b4aa93335 Fix retrace regex mentioned in the manual. 2025-08-13 14:56:37 +02:00
Nolij
4781f5898f Exclude annotation members from aggressive overloading (#453)
* Exclude annotation members from aggressive overloading
2025-08-11 09:15:43 +02:00
Jelle De Coninck
1f9a4a1b94 Remove AppSweep page from manual
Summary: Remove AppSweep page from manual and remove link from toolbar.
2025-06-13 15:52:18 +02:00
Blend Hamiti
40f9222bc3 Add mac finder files to gitignore 2025-05-02 16:28:52 +02:00
James Hamilton
ef6a8352bd Update ProGuardCORE version for Java 24 support (#470)
Update ProGuardCORE version for Java 24 support
2025-03-24 13:28:56 +01:00
niccolo.piazzesi
e225e56a8d Add link to the maven distribution and proguard release in the retrace page. 2025-03-24 11:25:13 +01:00
Ruben Pieters
4288cce536 Bump proguardCore version to include MethodLinker changes.
Includes the change from this proguard-core PR: https://github.com/Guardsquare/proguard-core/pull/133 .

Verified on the jar from the reproducing project: https://github.com/LlamaLad7/slow-proguard-example .
Takes ~45s before, ~10s after.
2025-03-19 12:04:53 +01:00
James Hamilton
3456cf330e Move source files to standard locations (#464) 2025-02-20 12:36:09 +01:00
niccolo.piazzesi
fbcf41fd67 Remove bad import 2024-12-13 14:26:54 +01:00
niccolo.piazzesi
bacde1cede Limit size of strings to 65535 bytes 2024-12-13 11:31:50 +01:00
Thomas Vochten
430a04502d Bump version to 7.6.2 2024-12-12 14:11:44 +01:00
Thomas Vochten
08adfa5552 Bump version to 7.6.1 in the manual 2024-12-12 12:13:01 +01:00
Thomas Vochten
89b1e55ea2 Add release notes for version 7.6.1, bump ProGuardCORE version 2024-12-12 11:27:45 +01:00
Bengt Verscheure
f4c4a13a90 Remove Guardsquare community link. 2024-12-10 15:53:19 +01:00
Thomas Vochten
c1eafc7b6b Log ACD rules for parameterless constructors 2024-12-06 12:47:35 +01:00
Thomas Vochten
73860de626 Discard empty Kotlin metadata 2024-12-06 12:47:35 +01:00
Thomas Vochten
ff66baaced Remove references to encryption 2024-12-04 12:30:55 +01:00
Thomas Vochten
174d3f4155 Upgrade Gradle, upgrade dependencies, bump version to 7.6.1 2024-11-29 08:49:38 +01:00
Thomas Vochten
f5352fece7 Clean up Kotlin verification in ProGuard.java 2024-11-27 09:36:51 +01:00
Bengt Verscheure
844f3d76be Remove variable push replacements optimization 2024-11-20 09:27:50 +01:00
niccolo.piazzesi
7b6712e840 Replace all PartialEvaluator constructor calls with builder calls 2024-10-25 16:19:41 +02:00
James Hamilton
dd4b8bde06 Update version to 7.6.0 2024-10-02 17:42:30 +02:00
James Hamilton
b3deed8286 Update versions for version 7.6 (#440) 2024-09-27 14:43:19 +02:00
alonalbert
8903bfb23f Separate Multiple Frames With a Newline (#433)
When an obfuscated frame resolves to multiple clear frames, separate them with a newline. Closes #432
2024-09-19 15:37:06 +02:00
Jelle De Coninck
03d7effdd2 Improve DictionaryNameFactory performance 2024-09-13 15:02:53 +02:00
James Hamilton
c2146ae315 Update ProGuardCORE version (#429) 2024-08-26 09:23:20 +02:00
James Hamilton
4e643b4f60 Update ProGuardCORE version (#421) 2024-07-18 12:48:54 +02:00
daphnis.chevreton
ee3deb69fa Support a wider char range in class specifications 2024-07-02 10:48:14 +02:00
daphnis.chevreton
6075d17bee Fix gradlew.bat newline chars 2024-07-02 10:48:10 +02:00
James Hamilton
3a9b11bb3c Bump version to 7.5.1 2024-05-29 18:07:33 +02:00
James Hamilton
af475c65b4 Update usage.md (#407) 2024-05-28 16:32:58 +02:00
James Hamilton
8d7ddf898c Update foojay resolver plugin 2024-05-27 11:29:21 +02:00
Ellet
f5f2f06334 Add .kotlin from Kotlin 2.0.0 in .gitignore (#406) 2024-05-27 09:05:12 +02:00
James Hamilton
aa43b9dc21 Update versions for 7.5 (#404)
Updates ProGuardCORE + other required dependencies for Kotlin 2 + Java 22; including running tests with Java 22.
2024-05-23 14:01:48 +02:00
Jelle De Coninck
0d9ceb7451 fix lint violations in ConfigurationParserTest 2024-05-06 11:29:28 +02:00
Jelle De Coninck
1d28c11e36 Synchronize keep flags on getter/setter/backing field for kotlin properties 2024-05-06 11:12:38 +02:00
Jelle De Coninck
b85b2cb201 Bump version to 7.5.0-beta01 and add release note 2024-04-30 16:58:42 +02:00
Jelle De Coninck
0c95982828 integrate kotlin 2 support
Summary:
* Update PGC
* Change use of kotlinx.metadata to kotlin.metadata
* Remove unused `KotlinModuleRewriter` class
2024-04-30 16:58:26 +02:00
James Hamilton
38de2e42b2 Update releasenotes.md 2024-04-19 09:50:00 +02:00
James Hamilton
76b2921738 Update ProGuardCORE version (#398) 2024-04-15 17:11:19 +02:00
niccolo.piazzesi
7483ad32f4 Keep name of Kotlin companion classes when corresponding field is kept and resolve potential name collision in KotlinCompanionEqualizer 2024-03-15 12:46:53 +01:00
niccolo.piazzesi
20c99aa3e8 Parse -maximumremovedandroidloglevel 2024-03-07 10:44:31 +01:00
Bentaii
858bcd0eb5 Fix equals to prevent null values (#388)
Switch frame.getSourceFile().equals(String) around to test String against source file value instead of otherway around to prevent  failures when the value is null.
2024-02-21 08:36:51 +01:00
Vincent Rossetto
1c421bf780 Keep Kotlin interface method when default implementation is used 2024-02-13 11:09:20 +01:00
James Hamilton
d4692c3835 Update version number in examples 2024-01-31 18:05:50 +01:00
James Hamilton
712fd768ca Add ability to skip to next option if parse error
Allows providing a function to handle the case of an unknown option.

```
    public static void main(String[] args)
    {
        try
        {
            try (ConfigurationParser parser = new ConfigurationParser(new String[]{"-keep class * {}", "-unknownoption", "-whatisthisoption?"}, System.getProperties()))
            {
                parser.parse(new Configuration(), (option, location) -> {
                    System.out.println("Unknown option: " + option + " @ " + location);
                });
            }
            catch (ParseException ex)
            {
                ex.printStackTrace();
            }
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
    }
```

Output:

```
Unknown option: -unknownoption @ argument number 2
Unknown option: -whatisthisoption? @ argument number 3
```
2024-01-22 14:55:53 +01:00
James Hamilton
c35913c3f2 Add release note 2024-01-19 18:41:05 +01:00
Cristian Garcia
5a8d50090a Replace project usages in ProGuardTask by injected Gradle services (#380)
fixes #254
2024-01-19 18:40:33 +01:00
James Hamilton
06c2d12f7a Bump version number 2024-01-16 09:36:28 +01:00
Oberon Swings
a7265a3536 Conservatively mark wide parameter used if it can only be partially marked. 2024-01-08 08:19:01 +01:00
Ruben Pieters
7160a9e484 Fix nullability flag on named type arguments and reified flag on type parameters 2023-12-07 16:49:40 +01:00
Robin Lefever
12c9c3f23e Fix MemberDescriptorSpecializer checking wrong processing flags
This diff also ensures the `IS_CLASS_AVAILABLE` processing flag is set on classes instead of its members.
This requires us to change usages of this flag in some other places as well.
2023-12-01 14:56:44 +01:00
daphnis.chevreton
a02100cb93 Fix potential access issues when backporting
Backporting nest host/members is not supported and can therefore introduce access issue.
Running the access fixer if `-allowaccessmodification` is set solves this issue by setting appropriate visibility.
2023-11-29 16:14:03 +01:00
Bengt Verscheure
38a0e498b9 Use system-specific line separators 2023-11-24 14:49:44 +01:00
James Hamilton
f6b82b1478 Update example versions to 7.4.1 2023-11-22 11:22:08 +01:00
Bengt Verscheure
789777ded5 Add support for <clinit> in ConfigurationParser 2023-11-17 13:40:46 +01:00
Dimitrios Anyfantakis
e76e47953f Close file handle in ConfigurationWriter 2023-11-03 12:28:07 +01:00
Fergal Whyte
836253f1da Use system-specific line separators in ConfigurationWriterTest 2023-10-26 13:51:26 +02:00
Fergal Whyte
f92fc632b1 Parse alwaysinline and identifiernamestring rules 2023-10-25 17:50:40 +02:00
Fergal Whyte
f5f04cbec5 Fix printing of hash characters in ConfigurationWriter 2023-10-25 17:50:40 +02:00
Fergal Whyte
ce2c8a8b5d Add ConfigurationWriterTest 2023-10-25 17:50:25 +02:00
Fergal Whyte
0032aa037c Prevent NullPointerException when parsing annotations 2023-10-25 17:44:19 +02:00
Fergal Whyte
0070bc9e80 Fix incorrectly configured unit tests 2023-10-25 17:44:11 +02:00
Sebastian Ratz
6f3610bd7b Fix inadvertent closing of System.out when printing configuration (#366)
Co-authored-by: Blend Hamiti <blend.hamiti@guardsquare.com>
2023-10-25 12:15:16 +02:00
James Hamilton
813616d095 Bump version number to 7.4.1 2023-10-25 11:07:22 +02:00
James Hamilton
04123e8f9b Update version number in home.md 2023-10-18 12:01:03 +02:00
James Hamilton
7c153f8eec Enable unit testing of Java 20 and 21 2023-10-12 12:59:11 +02:00
James Hamilton
76cf000348 Update version inside examples 2023-10-12 12:50:05 +02:00
James Hamilton
549e2cde2c Update version number in README 2023-10-12 11:34:29 +02:00
James Hamilton
f04ef27f66 Update version 2023-10-12 11:31:33 +02:00
James Hamilton
bfdfa02f8c Support parsing of wildcard * when used as a field type or method return type in class specifications. 2023-10-11 18:10:05 +02:00
Niccolò Piazzesi
58eae9eed5 Fix broken link to issue tab (#364) 2023-09-27 11:41:44 +02:00
Thomas Vochten
7429219cd2 Allow method from interfaces to be inlined if it is private and is being called from within the interface 2023-09-22 09:05:54 +02:00
Thomas Vochten
8bb7cc0c4b Do not inline methods from interfaces 2023-09-21 05:37:20 +02:00
James Hamilton
9a7a97d1ca Update ProGuardCORE version (#359) 2023-09-01 17:03:40 +02:00
James Hamilton
57d4250464 Use internalShortClassName in KotlinMultiFileFacadeFixer 2023-07-07 15:58:50 +02:00
James Hamilton
943e349f47 Update ProGuardCORE version (#343) 2023-06-13 17:45:03 +02:00
Joren Hannes
8afa59e7ce Keep methods that look like ClassValue's computeValue of Java 7 2023-06-08 15:26:37 +02:00
James Hamilton
14673bc36b Fix link to manual 2023-04-07 08:40:07 +02:00
Ellen Spertus
2b56fc6ced Make minor fixes to README.md (#330)
* Fix Windows command line
* Fix configuration link
2023-04-07 08:37:39 +02:00
James Hamilton
4cff876e44 Fix "NoClassDefFoundError: Failed resolution of: Lorg/apache/logging/log4j/LogManager #326 (#328)
* Remove log4j from injected classes

* Add release note
2023-03-30 13:00:41 +02:00
Emiel Matthys
1f8f548d36 Remove unused import in kotlin test 2023-03-24 16:45:21 +01:00
James Hamilton
09593afd21 Bump version to 7.3.3 2023-03-17 14:48:15 +01:00
James Hamilton
0dd91648be Remove ClassPrinter debugging from ClassUsageMarkerTest 2023-03-17 12:17:20 +01:00
James Hamilton
1b445599e0 Update manual 2023-03-17 11:35:45 +01:00
James Hamilton
aa1835fb93 Update ProGuardCORE (#325)
* Update ProGuardCORE

* Remove Java 20 from test list
2023-03-14 16:56:47 +00:00
James Hamilton
dda133e476 Add InstantiationClassMarkerTest (#320) 2023-03-02 13:28:39 +00:00
James Hamilton
bab3379536 Remove manual mentions of WTK plugin (#322)
The WTK plugin no longer exists.
2023-03-02 13:23:44 +00:00
tvoc-gs
764d35e4d5 Update manual (#321)
* Update manual

* Delete  and

---------

Co-authored-by: James Hamilton <james.hamilton@guardsquare.com>
2023-03-02 13:20:42 +00:00
tvoc-gs
745a681b32 Fix off-by-one error (#319) 2023-02-28 14:54:26 +00:00
Robin
12516ba710 Class merging only when optimizing aggressively 2023-02-03 09:08:45 +01:00
Robin
98309c9fbf Refactor ConfigurationChecker 2023-01-27 12:09:31 +01:00
James Hamilton
ad82f08b10 Bump version to 7.3.2 2023-01-16 09:50:26 +01:00
James Hamilton
f006f5810d Add Kotlin 1.8 to release notes 2023-01-13 12:45:24 +01:00
James Hamilton
dbdb423364 Add simple Kotlin example application 2023-01-13 12:44:36 +01:00
James Hamilton
fbda5d5156 Update tests to use KotlinMetadataVerifier
New Pass that replaces the old KotlinMetadataAsserter
2023-01-09 17:48:00 +01:00
James Hamilton
81561a0f2e Update ProGuardCORE version 2023-01-09 17:36:41 +01:00
Jelle De Coninck
8a7e846ec7 Fix string comparison in MappingPrinter and LineNumberLinearizer 2023-01-07 13:53:58 +01:00
James Hamilton
b9cf51b119 Update manual 2022-12-20 12:47:19 +01:00
James Hamilton
9b8f80229a Set the IS_CLASS_AVAILABLE processing flag 2022-12-14 17:23:52 +01:00
James Hamilton
1cbd6f7a68 Remove unused ExpectedStackTypeFinder 2022-12-14 17:23:52 +01:00
Thomas Vochten
3445fa02d6 Add -optimizeaggressively configuration option 2022-12-09 12:13:35 +01:00
Jelle De Coninck
4c6103eb2e Decrement uninitialized object count in MethodInliner when <init> is a LibraryMethod 2022-12-09 12:13:35 +01:00
James Hamilton
47e080f526 Update kotest dependencies 2022-12-09 12:13:35 +01:00
daphnis.chevreton
84ac82eb51 Fix linter issues 2022-11-24 15:25:10 +01:00
Ruben Pieters
2909a2ca7b Document supported/unsupported cases in TypeArgumentFinder. Fix incorrect invoke/getfield/getstatic case. 2022-11-24 15:24:58 +01:00
James Hamilton
bd98123633 Move release note to 7.3.1 2022-11-24 15:24:20 +01:00
James Hamilton
c4a5a89194 Update ProGuardCORE dependency 2022-11-17 11:07:12 +01:00
James Hamilton
3226de6d0c Expand imports in LambdaExpressionConverter 2022-11-17 11:07:07 +01:00
James Hamilton
20e3103518 Update ProGuard version in examples 2022-11-17 11:07:00 +01:00
James Hamilton
9c6eb91d01 Replace local constants with ProGuardCORE constants 2022-11-17 11:06:54 +01:00
James Hamilton
731ffd812d Conservatively keep annotation class constructor parameter names 2022-11-17 11:06:47 +01:00
James Hamilton
72f0ef13db Ensure that Kotlin parameters are kept when using -keepparameternames 2022-11-17 11:05:09 +01:00
Nadeesh T V
64c7322624 Add classintegrity asserter for enum entries 2022-11-16 15:58:41 +01:00
Jef Versavel
73cfa53192 Leave Unknown source during retrace 2022-11-16 15:58:37 +01:00
James Hamilton
34bc089656 Update version on manual homepage 2022-11-16 15:22:40 +01:00
James Hamilton
441574cc77 Bump version to 7.3.1 2022-11-15 18:18:24 +01:00
James Hamilton
1f74286f37 Merge branch 'beta' 2022-11-15 18:13:22 +01:00
James Hamilton
803b011589 Update GSON comments 2022-11-15 17:42:58 +01:00
James Hamilton
da479698e9 Update unsupported Kotlin version message 2022-11-15 17:42:50 +01:00
James Hamilton
33610e5ab0 Bump version to 7.3.0 2022-11-15 13:57:14 +01:00
daphnis.chevreton
ec3ce071bf Synchronise Kotlin companion and instance field before fixing name conflicts 2022-11-10 14:59:57 +01:00
James Hamilton
902a7f19c3 Update GSON comments 2022-11-09 13:50:34 +01:00
James Hamilton
ff74c14eba Update unsupported Kotlin version message 2022-11-09 13:47:56 +01:00
James Hamilton
7473a042cc Update GSON warning message (#293) 2022-11-09 13:45:01 +01:00
James Hamilton
cf99b22895 Update dependency versions (#287) 2022-10-21 18:27:22 +02:00
Toon Willemot
fe293dc318 Outlines the text in paragraphs of the manual 2022-10-21 18:26:45 +02:00
Robin
d32c159524 Update % wildcard meaning in ProGuard Class Specifications section of the manual 2022-10-21 18:26:30 +02:00
James Hamilton
f75147dfc3 Replace ProGuard Assembler dependency (#286)
It's now published to Maven Central. It's not needed directly but for the test fixtures which now has a transitive dependency on the Maven Central artefact.
2022-10-21 18:26:14 +02:00
James Hamilton
fe45cdfd5d Update release notes 2022-10-21 18:17:23 +02:00
Toon Willemot
bd63f2423f Automatically process Kotlin metadata when keeping the annotation
Add dontprocesskotlinmetadata configuration option.
2022-10-21 17:49:35 +02:00
Toon Willemot
cf5d492b00 Automatically process Kotlin metadata when keeping the annotation
Add dontprocesskotlinmetadata configuration option.
2022-10-20 17:28:21 +02:00
Robin
03c8b9f491 Update % wildcard meaning in ProGuard Class Specifications section of the manual 2022-10-20 17:13:48 +02:00
Toon Willemot
aca2004823 Outlines the text in paragraphs of the manual 2022-10-20 17:13:41 +02:00
James Hamilton
aa1936cc5f Mark context receiver parameters as used
To avoid the context receiver list becoming out of sync due to parameter shrinking, we now mark parameters at the same indices as context receivers as used.

This is a temporary solution and should be replaced with a solution that properly takes into account context receivers during optimization, so that e.g. unused context receivers can be removed.

~This diff adds support to the `MethodDescriptorShrinker` for shrinking the list of context receivers of a method.~

~There are some cases we cannot handle:~

~* properties where there is both a getter + setter~
~* functions with default parameters (in JVM land, a `$default` companion method)~
~* where the referencedMethod is null~

~Since we optimize each method independently, we can't be sure that the parameter lists are in sync when there are multiple JVM methods attached to a Kotlin entity like when there is a getter and setter together.~
2022-10-20 17:13:08 +02:00
James Hamilton
4ab73b2429 Update dependency versions (#287) 2022-10-14 17:01:47 +02:00
James Hamilton
8f491a9a56 Replace ProGuard Assembler dependency (#286)
It's now published to Maven Central. It's not needed directly but for the test fixtures which now has a transitive dependency on the Maven Central artefact.
2022-10-13 18:53:22 +02:00
James Hamilton
16d1b0ae87 Bump version to 7.3.0-beta2 2022-09-19 17:45:32 +02:00
James Hamilton
1c7f9628bb Merge branch 'master' into beta 2022-09-19 17:38:03 +02:00
James Hamilton
834ba4c363 Add release note for "ProGuard not working on Windows (#272)" 2022-09-19 13:57:23 +02:00
Simon Schiller
1b40f8d9cc Run CI on Windows in addition to Ubuntu (#277)
Co-authored-by: James Hamilton <mrjameshamilton@gmail.com>
2022-09-17 11:43:43 +02:00
James Hamilton
97e501d1b9 Add ClassUsageMarkerTest 2022-09-16 17:37:20 +02:00
James Hamilton
142e7d5cbf Take into account potential null default implementation references 2022-09-15 17:28:38 +02:00
James Hamilton
0e2b6002c5 Update version to 7.3.0-beta1 2022-09-09 17:41:36 +02:00
James Hamilton
51a6e71bfb Fix isPoppingExpectedType check
This should check the type against type instead of type against class name.
2022-09-09 15:00:03 +02:00
James Hamilton
3f8733d4fd Fix simple enum optimization (#275) 2022-09-01 16:52:36 +02:00
Pramitha Fernando
75b5e9854f Move logging out of DataEntryWriterFactory (#274)
Co-authored-by: Pramitha Fernando <pramitha.fernando@guardsquare.com>
2022-08-30 17:43:16 +02:00
James Hamilton
d532ce8812 Move logging out of DataEntryReaderFactory 2022-08-29 18:03:30 +02:00
James Hamilton
a1244e15f6 Write aar classes to lib folder, if not merging 2022-08-29 17:02:51 +02:00
James Hamilton
2d12f51b98 Add plain dex writing support to DataEntryWriterFactory 2022-08-26 17:55:58 +02:00
James Hamilton
d78af60166 Rename alternativeClassDataEntryWriterProvider 2022-08-26 15:20:28 +02:00
Pramitha Fernando
594507af03 update DataEntryReaderFactory & DataEntryWriterFactory 2022-08-26 15:20:28 +02:00
James Hamilton
3b4533faca Add basic Java 19 record pattern match test 2022-08-12 12:35:15 +02:00
James Hamilton
7bd1035f37 Remove Java 19-ea from test JVMs
It's not released yet, so won't be available via the Gradle toolchain
2022-08-12 12:35:15 +02:00
James Hamilton
33f70eb7f4 Add Java 19-ea support to release notes 2022-08-12 12:35:15 +02:00
James Hamilton
3ae37d6b87 Ensure test class version matches 2022-08-12 12:35:15 +02:00
James Hamilton
eb5a49f065 Replace duplicated test utils with ProGuardCORE test fixtures 2022-08-12 12:35:15 +02:00
James Hamilton
c3095256e4 Update ProGuardCORE dependency to 9.0.3 2022-08-12 12:35:15 +02:00
Emiel Vandeloo
f16430e3b3 dos2unix 2022-08-04 12:23:59 +02:00
James Hamilton
418828f7d4 Add system property to enable support for writing out zip64 archives.
Summary:
This diff enables support for writing out zip64 archives using ProGuard.
The zip64 support is enabled by default but can be disabled by setting the `disable.zip64.support` system property.

Test Plan: CI

Reviewers: jago.gyselinck

Reviewed By: jago.gyselinck

Subscribers: jago.gyselinck

Differential Revision: http://phabricator.guardsquare.com/D10612
2022-08-04 12:21:01 +02:00
James Hamilton
58d9b0b2dc Use a BufferedInputStream when parsing configuration debugging class maps 2022-07-28 09:52:20 +02:00
daphnis.chevreton
7a2680cebd Prevent merging classes with native methods (Port to proguard) 2022-07-27 15:46:37 +02:00
James Hamilton
96af8eb499 Add KotlinUnsupportedVersionChecker 2022-07-27 15:46:22 +02:00
James Hamilton
3a2c7b792e Initialize library class Kotlin metadata 2022-07-27 15:46:09 +02:00
James Hamilton
f2492965a0 Update to Gradle 7.5 (#268)
Allows enabling Java 9 testing again and additionally adding Java 18 testing.
2022-07-26 16:29:07 +02:00
Toon Willemot
04cc24fd16 Support Kotlin 1.7 2022-07-18 15:13:05 +02:00
Tony Robalik
3f8d78322d Don't fail when --configuration-cache is used. (#258) 2022-07-05 11:33:28 +02:00
Michael Vorburger ⛑️
6ef365a05e Fix broken link in README (#262) 2022-07-04 14:06:23 +02:00
James Hamilton
30040ac6af Update Gradle to 7.4.2 (#261) 2022-07-04 10:57:50 +02:00
James Hamilton
376d2bc663 Bump version to 7.2.3 2022-06-04 13:25:32 +02:00
James Hamilton
d6d9388b9b Append Log4J output to ProGuardGUI text area
Closes: #239
2022-06-04 13:03:26 +02:00
James Hamilton
6851aa67a7 Automatically set Java library jar based on Java version in ProGuardGUI 2022-06-04 11:17:47 +02:00
James Hamilton
4982541123 Update ProGuardGUI SourceForge reference 2022-06-04 11:15:59 +02:00
James Hamilton
b3c98ad729 Disable sorting of attributes
Closes: #192
2022-06-04 10:04:36 +02:00
James Hamilton
ec644d5c36 Switch callable reference fixing implementation using OptimizedCallableReferenceFilter 2022-06-04 09:59:27 +02:00
James Hamilton
78f50bba27 Update ProGuardCORE dependency to 9.0.1 2022-06-04 09:58:03 +02:00
Jelle De Coninck
f8d6fe5272 Fix lint
Fix ktlint for KotlinIntrinsicsReplacementSequencesTest.kt
2022-06-03 11:51:46 +02:00
Toon Willemot
4335f9cfcc Remove Kotlin Intrinsic strings by default
Move the KotlinIntrinsicsReplacementSequence stuff outside of the -keepkotlinmetadata option.
2022-06-03 11:30:17 +02:00
sleticalboy
e21af254d1 Update AndroidProjectBuilder.kt (#255)
String end expected
2022-06-03 08:54:07 +02:00
James Hamilton
6fd46cc0b2 KotlinMetadataAsserter: take into account Java 8+ interfaces default implementation 2022-05-31 12:10:24 +02:00
James Hamilton
2f65030bf4 Replace wildcard import 2022-05-31 12:09:21 +02:00
James Hamilton
451f883fce Use referencedMethodAccept in KotlinAnnotationFlagFixer 2022-05-31 11:56:39 +02:00
chevreto
b2e88ceed5 Improve UX for -target deprecation (#250)
* Improve UX for -target deprecation

Don't trigger the -target seatbelt when all classes are already at target version (no backport needed).
2022-05-27 19:21:06 +02:00
James Hamilton
d91599b709 Fix "No matching variant" issue when using Gradle 7.4 and Java 8 (#242)
* Set Java 8 version in main build.gradle for all projects

Closes: #231
2022-04-11 17:56:30 +02:00
James Hamilton
697ff01187 Remove extendsAnnotationType from ClassSpecificationVisitorFactory optimization (#236)
An optimisation was introduced in commit
ee0630eeed attempting to reduce the number
of classes visited by a visitor created by the
`ClassSpecificationVisitorFactory`.

The fix introduced a bug because the `extendsAnnotationTypeMatcher`
matches types like `LMyAnnotation;` rather than class names.

Closes: #229
2022-03-29 10:49:31 +02:00
James Hamilton
cfffb3e97d Fix "Can't save configuration file" error in ProGuardGUI. (#234) 2022-03-25 08:44:43 +01:00
Jef Versavel
e937aef822 Added null check in checkGetConstructor in the ConfigurationLogger. 2022-03-22 08:39:35 +01:00
Jef Versavel
364cdc342c Fix linting for KotlinIntrinsicsReplacementSequencesTest 2022-03-08 10:58:46 +01:00
Toon Willemot
662db60e71 Add throwUninitializedProperty intrinsics method to KotlinIntrinsicsReplacementSequences 2022-03-08 09:53:23 +01:00
Toon Willemot
48cee42937 Obfuscate toString methods of inline classes 2022-03-08 09:52:36 +01:00
Nadeesh T V
ccab80e755 Replace deprecated VersionNumber with semver.Version 2022-02-27 20:27:33 +01:00
Jelle De Coninck
59a74b2d51 Add support for R8's sourceFile mapping file comments. 2022-02-27 20:24:32 +01:00
James Hamilton
1f786a78a5 Bump version to 7.2.2 2022-02-18 19:44:59 +01:00
James Hamilton
87bb794331 Update example version to 7.2.1 2022-02-18 19:19:09 +01:00
James Hamilton
d0ce5b69e6 Fix KotlinMetadataVersion at 1.5.1 2022-02-18 19:04:38 +01:00
James Hamilton
36f6d1b929 Update release notes 2022-02-18 13:40:50 +01:00
James Hamilton
7dc4b6c576 Add shrinking & integrity support for Kotlin metadata synthetic delegate field 2022-02-18 13:39:46 +01:00
James Hamilton
d62e6a7c0c Add KotlinMetadataAsserter dangling class check 2022-02-18 13:38:38 +01:00
James Hamilton
cc1235c981 Update ProGuardCORE dependency to 8.0.7 2022-02-18 13:15:58 +01:00
Ulviyya Mammadzada
a1d13975f2 Reformat MarkedAnnotationDeleterTest 2022-02-17 12:16:27 +01:00
daphnis.chevreton
6bcd4a51d0 Deprecate backporter 2022-02-17 11:02:13 +01:00
Ruben Pieters
8d3c4e6b34 Fix index error in MarkedAnnotationDeleter. 2022-02-17 11:02:13 +01:00
James Hamilton
1035f18f65 Exclude androidx.window consumer rules by default 2022-02-11 16:59:55 +01:00
James Hamilton
6dc3ae5b98 Add consumerRuleFilter to ProGuard Gradle plugin 2022-02-11 16:59:55 +01:00
James Hamilton
609bf2d973 Update android-plugin sample ProGuard dependency to 7.2.0 2022-02-11 16:59:55 +01:00
James Hamilton
40fc9e24db Update android-plugin sample AGP version to 7.0.0 2022-02-11 16:59:55 +01:00
daphnis.chevreton
4ae53a2152 Prevent generation of windows reserved names 2022-02-11 16:59:55 +01:00
James Hamilton
f375d49348 Add section for 7.2.1 release notes 2022-02-11 16:59:55 +01:00
Emiel Vandeloo
ae60aad999 Allow inlining methods without optimization info 2022-02-04 15:26:34 +01:00
James Hamilton
e534c63c6e Update version to 7.2 in README.md 2022-01-28 15:56:30 +01:00
James Hamilton
250a51199c Update version to 7.2 in manual home page 2022-01-28 15:55:16 +01:00
anatawa12
d9ab10c273 Use canonicalPath instead of absolutePath to make test passed on macOS (#218) 2022-01-27 18:38:42 +01:00
James Hamilton
c85bfe4e43 Remove extra spaces. 2022-01-27 13:43:47 +01:00
James Hamilton
30e762fcf7 Bump version to 7.2.1 2022-01-26 22:51:17 +01:00
James Hamilton
587dcad79a Fix MarkerTest 2022-01-26 22:51:04 +01:00
James Hamilton
9505a50648 Merge branch 'beta' 2022-01-26 22:44:52 +01:00
maqsood ahmad
dc66dadf67 Move configuration out of AppView class.
Summary:
- This diff moves Configuration out the AppView class.
- All classes that use Configuration using AppView now get a Configuration reference in their constructors.
2022-01-26 18:46:44 +01:00
James Hamilton
0bfd2bd8ae Update release notes 2022-01-24 17:18:44 +01:00
James Hamilton
24e3863744 Update ProGuardCORE version to 8.0.6 2022-01-24 17:18:32 +01:00
James Hamilton
1adaf23e81 Update version to 7.2.0 2022-01-24 17:18:23 +01:00
James Hamilton
30b44ccc25 Remove use of deprecated constructor 2022-01-21 18:46:12 +01:00
maqsood ahmad
96a01c32d4 Use PassRunner to run passes.
- Add PassRunner, Benchmark, TimeUtil classes.
- Use PassRunner in ProGuard classes to run passes.
- All helper methods that run a pass throw Exception which is handled by its callers.
2022-01-17 17:26:03 +01:00
maqsood ahmad
1c5f806346 Move logging from ProGuard to respective passes/classes.
Move logging statements from the ProGuard class to the appropriate locations in their respective passes/classes.
2022-01-11 12:27:18 +01:00
maqsood ahmad
06cbcfcbbd Flatten passes - 2.
- Add NameObfuscationReferenceFixer pass
- Add ResourceFileNameAdapter pass
- Move nested KotlinMetadataAsserter pass from Initializer, Obfuscator and Shrinker to ProGuard class
2022-01-07 14:18:12 +01:00
James Hamilton
972bf45a04 Bump version to 7.2.0-beta7 2022-01-07 13:45:28 +01:00
James Hamilton
c7aac8af18 Add support for matching Kotlin inline classes with includedescriptorclasses 2022-01-07 11:39:40 +01:00
James Hamilton
6b169569cc Add ProcessingFlagUtil test utility 2022-01-07 11:39:40 +01:00
James Hamilton
84b2994c02 Update for Kotlin 1.6 support
This includes updates necessary to support Kotlin 1.6 including updating the ProGuardCORE dependency to 8.0.5
2022-01-07 11:39:36 +01:00
James Hamilton
0e3a3500bf Upgrade log4j2 dependency to 2.17.1 2022-01-06 18:31:27 +01:00
Ruben Pieters
dba190bf95 Split MethodInliner into separate subclasses. 2022-01-06 18:24:00 +01:00
Ruben Pieters
75b50830ff Split MethodInliner into separate subclasses. 2022-01-06 18:21:39 +01:00
maqsood ahmad
ec0d8f03ac Flatten passes. 2022-01-06 18:21:39 +01:00
Joren Van Hecke
ee0630eeed Replace usage of AllClassVisitor with a FilteredClassVisitor 2022-01-06 18:19:43 +01:00
maqsood ahmad
78a49d1e66 Passify classes after Obfuscator.
The following classes are changed/added.

- KotlinMetadataAdapter
- Targeter
- Preverifier
- LineNumberTrimmer
- OutputWriter
- Dumper
- ProGuard
2022-01-04 11:38:22 +01:00
maqsood ahmad
6350b26782 Passify Obfuscator and KotlinMetadataAsserter classes
The following classes are changed:
- Obfuscator
- ProGuard
- KotlinMetadataAsserter
- Initializer
- Shrinker
2022-01-04 11:38:22 +01:00
maqsood ahmad
d651c0f4c8 Passify Optimizer and LineNumberLinearizer
Passify Optimizer and LineNumberLinearizer

The following classes are changed:

- Optimizer
- ProGuard
- LineNumberLinearizer
2022-01-04 11:38:22 +01:00
maqsood ahmad
3df20f6b32 Passify classes between PrimitiveArrayConstantIntroducer and GsonOptimizer.
The following classes are changed

- ProGuard
- SeedPrinter
- Backporter
- PrimitiveArrayConstantIntroducer
- ConfigurationLoggingAdder
- GsonOptimizer
- SubroutineInliner
- Shrinker
2022-01-04 11:38:22 +01:00
maqsood ahmad
9df9fcdb04 Port changes to PreverificationClear up to KotlinAnnotationStripper classes.
Implement Pass interface in PreverificationClearer up to KotlinAnnotationStripper classes.

- Initializer
- ProGuard
- Marker
- PreverificationClearer
- KotlinAnnotationStripper
2022-01-04 11:38:22 +01:00
maqsood ahmad
45798d2f96 Add Pass/AppView classes and port changes upto clearPreverification.
Add AppView and Pass classes

Port changes to the following:
- InputReader
- ProGuard
- UpToDateChecker
- ProGuardGUI

Ignoring the changes for:
- ConfigurationChecker
- ConfigurationParser and ConfigurationParserTest
- ConfigurationWriter
- GPL
2022-01-04 11:38:22 +01:00
James Hamilton
d8260f892c Add command to build ProGuard from source to README 2021-12-22 09:05:31 +01:00
James Hamilton
be163ee86e Bump version to 7.2.0-beta6 2021-12-21 10:49:25 +01:00
James Hamilton
d43e4c1a3c Upgrade log4j2 dependency to v2.17 in response to CVE-2021-45105 2021-12-21 10:43:49 +01:00
James Hamilton
cb4127be4d Update ProGuardCORE dependency version to 8.0.4 2021-12-21 10:42:20 +01:00
James Hamilton
2822edc08a Bump version to 7.2.0-beta5 2021-12-16 15:36:56 +01:00
James Hamilton
88a57e90af Upgrade log4j2 dependency in response to CVE-2021-45046 2021-12-16 09:18:47 +01:00
James Hamilton
0cd72ef0b5 Update release note for T7056 2021-12-14 08:43:48 +01:00
James Hamilton
8d351087ec Bump version to 7.2.0-beta4 2021-12-14 08:43:15 +01:00
James Hamilton
2c224c1816 Update ProGuardCORE dependency to 8.0.2 2021-12-13 18:18:08 +01:00
James Hamilton
ada8827e25 Upgrade log4j2 dependency in response to CVE-2021-44228 2021-12-13 18:17:44 +01:00
James Hamilton
cf23e5a80d Merge branch 'master' into beta 2021-12-13 15:45:42 +01:00
lintao
2d012a9ee2 Fix: can only retrace once, forget reset systemOutRedirected #69
Co-authored-by: James Hamilton <mrjameshamilton@gmail.com>
2021-11-11 16:22:52 +01:00
James Hamilton
667cb2e1a8 Merge branch 'master' into beta 2021-11-11 16:04:43 +01:00
LoneDev
b0104ecd96 Quality of life features (#200)
ReTrace

- Initial support for Java 16 stacktraces

GUI

- Save the last used mappings path
- Fixed ReTrace not working anymore after first usage
- Save window location and size
2021-11-11 16:04:17 +01:00
James Hamilton
3e180e5c67 Update building.md 2021-11-11 15:45:09 +01:00
Toon Willemot
c6b2bef146 Visit referencedField of EnumConstantElementValue during usage marking 2021-10-05 11:24:22 +02:00
James Hamilton
0595e0e2d1 Clarify use of class name * in a -keep rule (#191) 2021-09-22 11:23:12 +02:00
James Hamilton
94a56c4058 Merge branch 'master' into beta 2021-09-03 14:25:32 +02:00
Joren Van Hecke
121ac840a7 Fix test for non-existing ProGuard Gradle Plugin configurations.
Fix test for non-existing ProGuard Gradle Plugin configurations. The test was broken, after the error message had been changed.
2021-09-03 14:25:12 +02:00
James Hamilton
c4f4e3176c Merge branch 'master' into beta 2021-09-02 15:48:51 +02:00
Joren Van Hecke
7b7e4aaafc Correct error message for invalid configuration in ProGuard Gradle Plugin. 2021-09-02 15:46:06 +02:00
Joren Van Hecke
a40b8326a1 Use correct default configuration when specifying proguard-android-optimize.txt 2021-09-02 15:46:06 +02:00
Joren Van Hecke
b8f1552823 Create logger in proguard.optimize.Optimizer with getLogger instead of getFormatterLogger. 2021-09-02 15:42:54 +02:00
Joren Van Hecke
9896fefe16 Correct error message for invalid configuration in ProGuard Gradle Plugin. 2021-09-02 15:42:54 +02:00
James Hamilton
fbdf041710 Add missing # anchor in callback link 2021-08-31 13:27:09 +02:00
Joren Van Hecke
f3d9b802be Add logging to proguard.io, proguard.backport, proguard.configuration, proguard.optimize, proguard.shrink and proguard.strip packages 2021-08-17 11:54:36 +02:00
Joren Van Hecke
eaf0443293 Add logging to proguard.optimize.peephole package 2021-08-17 11:54:36 +02:00
Joren Van Hecke
502fb454de Add logging to package proguard.optimize.info 2021-08-17 11:54:36 +02:00
Joren Van Hecke
e4dba1d86a Add logging to package proguard.optimize.evaluation 2021-08-17 11:54:36 +02:00
Joren Van Hecke
2a4114cfd8 Add logging to proguard.optimize package 2021-08-17 11:54:36 +02:00
Joren Van Hecke
c732f3d8d2 Add logging to package proguard.optimize.gson 2021-08-17 11:54:36 +02:00
Joren Van Hecke
46d080b45f Add logging to package proguard 2021-08-17 11:54:36 +02:00
Joren Van Hecke
50151e643c Add log4j2 dependency to ProGuard 2021-08-17 11:54:36 +02:00
James Hamilton
7a4e35559b Fix obfuscated jar filename in spring-boot example 2021-08-17 11:34:43 +02:00
James Hamilton
61b769a5e8 Bump version to 7.2.0-beta3 2021-08-13 15:49:36 +02:00
James Hamilton
a830e358ee Update ProGuardCORE dependency version to 8.0.1 2021-08-12 20:49:13 +02:00
James Hamilton
68d0c4b725 Update issue number in release notes 2021-07-28 09:02:52 +02:00
James Hamilton
45d78d83d6 Update version number in release notes 2021-07-26 20:28:46 +02:00
James Hamilton
2d806bbfad Add multi-release: true to jar artifcats
Required, due to log4j2 included in ProGuardCORE 8.0.0
2021-07-26 20:17:14 +02:00
James Hamilton
5b02980ffe Update version to 7.2.0-beta2 2021-07-26 15:50:40 +02:00
James Hamilton
2e1b0ce816 Add beta to CI workflow 2021-07-26 15:46:35 +02:00
James Hamilton
f01d3857b5 Update ProGuardCORE dependency to 8.0.0
Updates ProGuard to support Kotlin 1.5
Builds with ProGuardCore 8.0.0
2021-07-26 15:46:27 +02:00
James Hamilton
c2b8dcb869 Update version to 7.2.0-beta1 2021-07-26 15:46:24 +02:00
James Hamilton
e0200f0bfa Bump version to 7.1.2 2021-07-26 14:33:20 +02:00
Toon Willemot
b4ab30a5b7 Update ProGuard to 7.1.1 2021-07-26 14:06:39 +02:00
Toon Willemot
806cf96d83 Update Gradle deprecation in ProGuardCacheRelocateabilityIntegrationTest 2021-07-23 16:38:11 +02:00
James Hamilton
7c9c097fea Merge base unit test jacoco coverage reports 2021-07-23 16:38:11 +02:00
James Hamilton
919465a0a4 Run base unit tests on multiple Java versions
Port of testing updates from ProGuardCORE:

* Updates to ClassPoolBuilder testutil
* Ability to run tests on multiple Java versions
* Update test dependencies
* Update CI to execute all Java version tests
2021-07-23 16:38:11 +02:00
James Hamilton
79c6b926ab Update Gradle wrapper version to 7.1.1 2021-07-23 16:38:11 +02:00
James Hamilton
ca21767df8 Fix incorrect handling of InterruptedException in InfluenceFixpointVisitor
Fix incorrect handling of InterruptedException in InfluenceFixpointVisitor.
2021-07-23 16:38:11 +02:00
Jelle De Coninck
2cc79cb84c Refactor ClassMerger to extract mergeability check into separate method
Includes new `ClassMergerTest`
2021-07-19 14:55:45 +02:00
James Hamilton
aeae2d3a3e Add second CallableReference initialization sequence pattern 2021-07-19 14:55:45 +02:00
James Hamilton
71176d97da Add support for Kotlin 1.4 callable references to KotlinCallableReferenceFixer 2021-07-19 14:55:45 +02:00
James Hamilton
2d17ed8c8e Add KotlinCallableReferenceFixerTest
Unit test for `KotlinCallableReferenceFixer` - passes on Kotlin 1.3, fails on Kotlin >= 1.4
2021-07-19 14:55:45 +02:00
James Hamilton
bf8669cd92 Port unit testing infrastructure updates from ProGuardCORE 2021-07-19 14:55:45 +02:00
James Hamilton
a450a25619 Build against a fixed ProGuardCORE version
With this change the stable ProGuard branch remains stable, independent
of changes in ProGuardCORE. This avoids the stable branch of ProGuard
failing if breaking changes are pushed to ProGuardCORE master branch.
2021-07-19 14:55:45 +02:00
James Hamilton
e427ecce57 Update version number in README 2021-07-08 23:04:14 +02:00
James Hamilton
6dfa0ca00e Refactor KotlinValueParameterNameShrinker
This refactors `KotlinValueParameterNameShrinker` to remove the need for
the strange `onNewFunctionStart()` method. This will allow for removal
of `onNewFunctionStart()` since it's the only place that it's used in
DexGuard and ProGuard. `onNewFunctionStart()` does not fit into the rest
of the visitor API and the use-case is better handled as in this
refactor. Additionally the name `onNewFunctionStart()` does not make
sense since it could be a function, constructor or a property.
2021-07-05 17:40:09 +02:00
James Hamilton
29d96d4304 Add ProGuard Assembler functionality to unit test infrastructure 2021-07-05 17:40:09 +02:00
James Hamilton
e0406d77c3 Port unit testing infrastructure from ProGuardCORE
Includes 1 basic test from DexGuard tests: `ConfigurationParserTest`.
2021-07-05 17:40:09 +02:00
James Hamilton
c77c1d7dd5 Create proguard-app project
This is a minor reshuffle:

* base project was actually already a library but an application was created by creating a fat jar within the library project
* with the new proguard-app project, there is now a clear distinction conceptually between the base library project and the application project

Nothing else changes:

* proguard-base is still published to maven
* proguard.jar is still built as a fat jar for the archive distribution
2021-07-05 17:40:09 +02:00
James Hamilton
802274b03a Move Java source files to standard location
Previously, they were located in a non-standard location and therefore
required specifying the location manually with a sourceSets block.
2021-07-05 17:40:09 +02:00
Ruben Pieters
82639a4290 Throw exception when encountering IncompleteHierarchyException. 2021-07-01 09:13:30 +02:00
James Hamilton
d914fa94fc Update version to 7.1.1 2021-06-29 17:03:31 +02:00
James Hamilton
91e0206127 Update version to 7.1.0 2021-06-29 12:37:11 +02:00
James Hamilton
fcde4bfd92 Fix links to Gradle plugin instructions manual page 2021-06-29 12:31:26 +02:00
Laurent Ferier
97928776ca Update ProGuard 7.1 release notes
Use the current guidelines for the release notes
2021-06-29 09:34:47 +02:00
James Hamilton
46b2c8b915 Use FQ named for ClassRenamer
ClassRenamer will be moved to ProGuardCORE. This commit temporarily
uses the FQ name of the old ClassRenamer before any refactoring (and
before commiting to the ProGuardCORE master branch, so as not to clash
with the new proguard.classfile.util.ClassRenamer class.
2021-06-28 16:44:45 +02:00
Ruben Pieters
847683d5ee Assert that the InfluenceFixPointVisitor always uses at least one thread. 2021-06-23 11:52:10 +02:00
Jago Gyselinck
1c8d1b151c Restore the 'specifications' section in the ReTrace manual
This diff restores the 'specifications' section of the ReTrace
manual, which was accidentally removed when we merged the
different ReTrace manual pages into one.
2021-06-23 11:52:10 +02:00
James Hamilton
efd665afac Fix collection of AAPT rules 2021-06-15 16:08:57 +02:00
James Hamilton
601affb88b Upgrade proguard-gradle module clash warning to error if newer ProGuardTask method is missing 2021-06-15 16:08:57 +02:00
James Hamilton
da524a3499 Change plugin ID from proguard to com.guardsquare.proguard 2021-06-15 16:08:57 +02:00
James Hamilton
689fdaaadb Re-add deprecated KeepClassSpecification constructor
The AGP integration might use this constructor that was removed during a
refactor when using the legacy ProGuard integration method (dependency
substition).
2021-06-15 16:08:57 +02:00
James Hamilton
5650e9e06a Add legacy AGP ProGuard integration test 2021-06-15 16:08:57 +02:00
Jelle De Coninck
9c3027ad43 Added warning when old proguard is found on classpath
Detect when an old version of proguard (net.sf.proguard:proguard-gradle) bundled with AGP < 7 is on the classpath, and print a warning showing how to exclude it.
2021-06-15 16:08:57 +02:00
James Hamilton
60da6a571e Fix Kotlin lint issues in gradle-plugin 2021-06-15 16:08:57 +02:00
Jelle De Coninck
021bbb1f82 Add task to create directory for aapt_rules.pro 2021-06-15 16:08:57 +02:00
Daz DeBoer
e8e749ce33 Whitelist jdk.internal.reflect package in GPL warning (#165)
JDK8 classes in `sun.reflect` have been relocated to `jdk.internal.reflect` in JDK9+.
This change ensures that a GPL warning is not printed when `jdk.internal.reflect`
is detected in linked packages.
2021-06-02 09:12:58 +02:00
James Hamilton
ff5f6edfdb Update upgrading manual page to refer to current released version (#168) 2021-05-31 14:06:20 +02:00
James Hamilton
25bcbdbc37 Update version to 7.1.0-beta6 2021-05-27 23:33:52 +02:00
James Hamilton
811cdfc6be Fix Gradle Plugin publication config (#167) 2021-05-27 22:59:17 +02:00
James Hamilton
b0f0ae6d1b Exclude example project build and .gradle folders from distribution archives 2021-05-27 17:58:17 +02:00
James Hamilton
898d126a38 Update 7.1 release notes to use imperative 2021-05-27 17:19:17 +02:00
James Hamilton
19ec404959 Reword release note for DGD-3377 2021-05-27 17:19:13 +02:00
James Hamilton
bc10bcdd51 Update README.md with quickstart instructions for Gradle plugin 2021-05-27 17:19:08 +02:00
James Hamilton
5c0478b6a1 Add aapt generated rules in Gradle Plugin 2021-05-27 17:19:05 +02:00
James Hamilton
a7462c9e77 Fix potentially incorrect class merging optimizations that could lead to merging unused code 2021-05-27 17:19:00 +02:00
James Hamilton
566026eba6 Add ProGuard config files to secondary inputs 2021-05-27 17:18:55 +02:00
James Hamilton
de986fad86 Remove needless blank lines 2021-05-27 17:18:51 +02:00
James Hamilton
27bf5fc814 Update product name in optimizations manual page 2021-05-25 17:45:49 +02:00
James Hamilton
230d3920de Fix typo in optimizations manual page 2021-05-25 17:45:49 +02:00
James Hamilton
1a7959d978 Add checks for configured variants 2021-05-25 16:51:41 +02:00
James Hamilton
6cf1999f52 Create collectConsumerRulesTask only when variant is configured 2021-05-25 16:51:31 +02:00
James Hamilton
6330b9eff9 Include configuration files in the order they were specified 2021-05-25 16:51:21 +02:00
James Hamilton
d9c64430b4 Fix formatting issues in gradle-plugin tests 2021-05-25 16:51:11 +02:00
James Hamilton
bece91266b Add upgrading manual page 2021-05-25 16:51:00 +02:00
James Hamilton
465304515b Fix minifyEnabled = true check that checked all variants 2021-05-25 16:50:50 +02:00
Sven Cuyt
8b1bd44bca Add a check of the AGP version 2021-05-25 16:50:36 +02:00
James Hamilton
4d84aa543f Add check to verify that minifyEnabled = false configured variants 2021-05-25 16:50:02 +02:00
James Hamilton
5f69246e3a Update ProGuard Gradle plugin to be compatible with AGP 7 2021-05-25 16:49:18 +02:00
James Hamilton
e00da102f4 Add internal extraJar option 2021-05-25 16:48:27 +02:00
James Hamilton
036291e8fb Update version to 7.1.0-beta5 2021-05-18 00:24:58 +02:00
James Hamilton
98813c4181 Move -addconfigurationdebugging warning message 2021-05-17 16:13:25 +02:00
James Hamilton
e3e63607ac Remove unused ExtraDataEntryWriter 2021-05-17 16:11:35 +02:00
James Hamilton
7ed4eb92a6 Update comments in ConfigurationLogger 2021-05-17 16:11:35 +02:00
James Hamilton
d82a4c4c13 Remove unused files
Incorrectly added in previous commit
2021-05-17 16:11:35 +02:00
James Hamilton
13f824e2aa Reduced false positives when using -addconfigurationdebugging 2021-05-17 16:11:35 +02:00
James Hamilton
976b998c74 Remove extra ` character in example manual page markdown 2021-05-15 15:55:19 +02:00
James Hamilton
b4d085ce60 Fix Android admonition entries in manual (#161)
This commit adds a custom Android admonition so that the Android note admonitions are rendered correctly in the manual.
2021-05-10 10:08:31 +02:00
Benedikt Ritter
dd1a2583f9 Make ProGuardTask output properties public (#159)
With this change users can now wire up ProGuardTask with other tasks. Gradle will then automatically track the task dependency between ProGuard and the consuming task.
2021-05-06 15:34:26 +02:00
James Hamilton
9d08ab1a26 Update version to 7.1.0-beta4 2021-04-30 16:36:26 +02:00
James Hamilton
f21df09a64 Add maven publication descriptions (#157) 2021-04-30 14:44:57 +02:00
Eric Salemi
9fd8c16bfb Fix publication signing (#156) 2021-04-30 11:33:19 +02:00
James Hamilton
55713a9f9a Update ProGuard logo and URL (#155) 2021-04-28 16:44:26 +02:00
James Hamilton
87000a49ec Update jcenter references to Maven Central (#154) 2021-04-28 16:43:22 +02:00
Eric Salemi
77d734f458 Change README badge from JCenter to Maven Central (#153) 2021-04-28 15:01:45 +02:00
Eric Salemi
b5acb79b11 Change publishing from Bintray to Sonatype (#152)
Co-authored-by: Eric Salemi <eric.salemi@guardsquare.com>
2021-04-28 09:54:45 +02:00
Daz DeBoer
cb7928d19a Simpify proguard.gradle.ProGuardTask implementation (#151)
* Revert "Improve handling of Gradle task inputs and outputs (#139)"

This reverts commit b5fbefb87c.

* Add more test coverage of Gradle plugin

- Rename `gradlekotlindsl` sample to `gradle-kotlin-dsl`
- Use `gradle-kotlin-dsl` as basis for plugin testing
- Add coverage for loading from external config file

* Register Proguard output files as Gradle task outputs

These outputs file may be consumed by other Gradle tasks, and
this change makes the output file locations available programatically.

We use this internally to publish the generated mapping file to internal
repository, which makes it easier to later deobfuscate stack traces. eg:

publishing {
    publications {
        register("sourcemap", MavenPublication) {
            artifact tasks.named("proguard").map { it.printMappingFile }
        }
    }
}
2021-04-27 17:56:23 +02:00
Daz DeBoer
3db816337f Remove static cache for generated names in SimpleNameFactory (#149)
* Remove static cache for generated names

The `SimpleNameFactory` was using a static List in an attempt to improve performance,
by avoiding the need to recreate the String name multiple times. The implementation
was not thread-safe, and could result in invalid names being produced when Proguard
was being run concurrently within the same classloader.

When 2 threads entered `getName()` simultaneously, they could both construct the same
name value for the same index, and both would then add these names to the static Map.
This resulted in a name List like `['a', 'a', 'c', ...].

This bug would manifest itself with a `Duplicate Jar Entry` exception, due to the fact
that `SimpleNameFactory` would return the same name value on subsequent calls.

This commit merely removes the optimization. If name generation is demonstated to be
a performance bottleneck, and safer optimization should be implemented, ensuring
correct synchronization on shared resources.
2021-04-26 17:02:44 +02:00
Eric Salemi
97451730f3 Disable extraneous plugin publication (#147)
Co-authored-by: Eric Salemi <eric.salemi@guardsquare.com>
2021-04-15 09:36:59 +02:00
Eric Salemi
935e7e79c0 Remove proguard-parent artifact (#45)
Co-authored-by: Eric Salemi <eric.salemi@guardsquare.com>
2021-04-15 09:19:50 +02:00
Eric Salemi
308dec7d6e Remove developer URL and change name in POM publication (#146)
Co-authored-by: Eric Salemi <eric.salemi@guardsquare.com>
2021-04-12 18:44:17 +02:00
James Hamilton
f16d3d8cb9 Improve error message when missing classes result in an incomplete class hierarchy 2021-04-09 17:40:14 +02:00
James Hamilton
a58c096046 Add missing optimization info to GUI resources
New optimizations were added in 7.1.0-beta2 but the GUI resources were
not updated.

Closes: #144
2021-04-09 13:22:47 +02:00
James Hamilton
0344c58b3d Fix marking of classes that contain constructors during optimization
Closes: #141
2021-04-05 13:42:28 +02:00
James Hamilton
bd0327e088 Update Kotlin version to 1.3.72 2021-04-02 17:04:08 +02:00
James Hamilton
12fd4ae1d9 Update version to 7.1.0-beta3 2021-03-30 22:19:34 +02:00
Daz DeBoer
b5fbefb87c Improve handling of Gradle task inputs and outputs (#139)
Previously, Task inputs and outputs were declared in an ad-hoc manner,
resulting in confusing source code and inconsistent behaviour with up-to-date checks. 

Specifically:

- If an input (`-injars`, `-libraryjars`) was configured via a ProGuard config file, 
  then changes to these inputs were not detected as part of up-to-date checks
- Similar for output files: if an output added with `-outjars` was deleted, the task
  would not detect this and would not re-execute
- Many task inputs were not registered at all, meaning that the task would be incorrectly
  considered up-to-date when these inputs were changed (eg `keep`, `dontshrink`, 
  `assumeNoSideEffects`, `obfuscationDictionary`, etc)
- Many output files were not tracked at all (eg printMapping). This meant that changes
  to these files were not detected, and they were not restored correctly from cache.

A few further notes:

- To avoid having to wrap the `ClassSpecification` and `MemberSpecification` types
(and subtypes), these classes were declared as `Serializable`. 
- The existing properties on `ProGuardTask` were left unchanged, to avoid changing the
  task API and to retain support for the existing DSL configuration.
- Since `programJars` and `libraryJars` can contain a mix of files and directories, 
  inputs and outputs, each entry is exposed as a `WrappedClassPathEntry`, with subtypes
  representing the actual input/output file/directory.
- Use a `Provider<Configuration>` to defer initial instantiation of `Configuration`
- Provider is a mapping from configuration input files, which means it will carry
  task dependency information 
- Provider implementation loads the configuration on first use, memoizing for later uses.

* Simplify the task configuration model to make it possible to defer loading configuration

The first attempt modeled each input file as a `@Nested` object containing the File
reference as well as the filters attached to that file. While this closely modelled
the true configuration, it meant that each nested instance was instantiated early
in order to inspect the properties.

With this change, all input files are merged into a single `FileCollection` property,
and the filters for all files are merged into a separate `List<Serializable>` property.
This structure will later allow us to defer loading these property values until the
task begins executing.

* Correctly handle output directories

By borrowing the file detection logic from Proguard core, we can determine if
a configured output file is a file (with a known extension) or a directory.
This is required to correctly register the file as an `OutputFile` or `OutputDirectory`.

Using `.isFile()` and `.isDirectory()` is not sufficient, as the file may not exist.

* Defer loading of Proguard configuration until task is executed

By default, all `@InputFile` properties are resolved when building the task
graph. By declaring these input properties as a mapping from the configuration-file
inputs, the provider contains enough context to avoid realizing the actual properties.

Since realizing these properties involves loading the Proguard configuration, this
change allows this work to be deferred until the task inputs are actually calculated
as part of task execution.

* Track direct task inputs separately, to preserve task dependencies

The input files for the proguard task are a combination of files loaded via a
configuration file and files directly configured via the Gradle DSL. The latter
can carry task dependency information, but this is being lost when we merge
the set of input files.

This change adds separate `@Classpath` properties to the task to track the files
added directly via the Gradle DSL, thus preserving task dependencies for these inputs.

* Workaround limitation in Gradle 6.x

In older versions of Gradle, all `InputFile` properties of type `Provider`
are realized when building the task graph in order to check for existence.
This happens even when these were mapped from a known producer, resulting in
early loading of the ProGuard configuration file(s).

In order to workaround this issue, a custom `Provider` implementation is
used for Gradle versions <= 7.0

* Renamed from `gradlekotlindsl` -> `gradle-kotlin-dsl`

Closes: #139, #106, #136
2021-03-27 17:03:47 +01:00
James Hamilton
8a02ef694c Update examples/android sample 2021-03-25 23:03:32 +01:00
James Hamilton
26a3dd497b Update release notes with details of optimization updates 2021-03-25 00:25:34 +01:00
James Hamilton
2ba28f1a17 Improve optimizations
Includes the addition of 2 new field optimizations and 3 new method
optimizations
2021-03-24 08:40:52 +01:00
James Hamilton
af272c24b6 Updated release notes 2021-03-09 10:49:19 +01:00
James Hamilton
318efa6111 Update version to 7.1.0-beta2 2021-03-09 09:25:28 +01:00
James Hamilton
3867b50091 Update version to 7.1.0-beta1 2021-03-09 09:15:32 +01:00
Daz DeBoer
6afe222955 Improve build for local development and add missing API dependencies (#131)
* Document persisting the composite build layout using `gradle.properties`

* Speed up the build with not-very-experimental options

- Parallel project execution
   - This reduces `clean assemble` by 50%
- Local build cache
   - This reduces a no-op `clean assemble` by a further 60%
- File system watching
   - This will be enabled by default in Gradle 7, and speeds up builds using
     the same daemon process

* Add a settings file so that proguard-gradle examples can be run

* Document the process to publish artifacts locally for testing

* Include `proguard-core` in the API dependencies of `base`

Many of the public types in the `proguard-base` module export types that
are defined in the `proguard-core` module. This means that these `proguard-core`
types are required when compiling againt `proguard-base`.

By declaring `proguard-base` as a `java-library` and including `proguard-core`
as an API dependency, consumers of `proguard-base` do not have to declare this
explicit dependency.

* Include `proguard-base` as an API dependency of `proguard-gradle`

The public type of `ProGuardTask` exports a number of types defined in
the `proguard-base` module (`Configuration`, `ParseException`, etc). These types
are required when compiling against `ProGuardTask`, and can result in compilation
error when missing.

The correct way to model this in Gradle is to include `proguard-base` as an
API dependency of `proguard-gradle`.
2021-03-02 12:39:26 +01:00
Daz DeBoer
3da6b8c2a0 Only log informational output when running with verbose=true (#130)
The `DataEntryReaderFactory` and `DataEntryWriterFactory` were both writing
informational messages to stdout in a way that made these messages difficult
to suppress. With this change, these messages are only displayed when
the `verbose` setting is enabled on the Proguard configuration.
2021-03-01 17:42:23 +01:00
James Hamilton
b5f5d666dc Add condition around the maven signing block 2021-03-01 12:25:49 +01:00
James Hamilton
cd5463d12d Fixed potential incorrect removal of exception handlers during optimization 2021-03-01 12:25:43 +01:00
James Hamilton
89e1d133fc Improve GSON support
Adds various GSON fixes.
2021-02-22 14:43:07 +01:00
James Hamilton
eddc8a2f87 Fixed potential, incorrect advanced code optimizations
Applies fixes to the EvaluationShrinker to take into account JVM
verification and complex pop/dup sequences.
2021-02-22 14:43:00 +01:00
James Hamilton
3ea6f22730 Disallow merging of nest hosts and members during optimization
Closes: #37
2021-02-22 14:42:41 +01:00
Johannes Bühler
3856c91c60 Fix Gradle 7 deprecations (#123)
* ensure Gradle 7 compatibility by removing deprecation warnings and update to latest Gradle 6.8.x
2021-02-09 16:57:45 +01:00
Philippe Guislain
5b01532d67 added reference to the Community in the PG manual 2021-01-27 15:08:58 +01:00
Philippe Guislain
75136d9aec Add language markers to code snippets in the manual 2021-01-27 15:08:58 +01:00
James Hamilton
52f110ef5d Fix packaging of Ant plugin (#121)
Added fat jar to the distribtion zip and updated documentation to the new name
2021-01-27 12:02:04 +01:00
Nelson Osacky
613af185f3 Add cache relocatability to proguard task. (#119)
* Add cache relocatability to proguard task.

This allows the Proguard Task to take advantage of the Gradle Build cache and re-use outputs from other builds.

This also adds a simple test for cache relocateability

* Address review comments

* Use regex to replace build.gradle file

* Use jar in spring boot sample.
2021-01-26 21:18:34 +01:00
Philippe Guislain
acacab39be Update ReTrace image in manual page 2021-01-18 16:56:34 +01:00
Philippe Guislain
8a21998eaa Updated manual structure and combined ReTrace pages 2021-01-18 16:56:34 +01:00
Philippe Guislain
0cd13bd9d9 Update quickstart and setup pages 2021-01-18 16:56:34 +01:00
James Hamilton
0d83884213 Add docs/html to gitignore 2021-01-18 16:56:34 +01:00
James Hamilton
0fe9ab93e9 Update mkdocs markdown extensions 2021-01-18 16:56:34 +01:00
James Hamilton
c4e9d3f56d Fix IllegalArgumentException in GSON optimization 2021-01-18 09:19:58 +01:00
Nelson Osacky
37f55f37d2 Update Gradle to 6.8 (#117)
Update Gradle to 6.8
2021-01-15 13:08:02 +01:00
Nelson Osacky
737bf24843 Add integration test for Gradle Plugin and Spring Boot. 2021-01-14 17:47:27 +01:00
Ingmar Dasseville
f7c5ebc443 Remove the CI token for proguard-core checkout 2021-01-12 16:41:29 +01:00
James Hamilton
9b16748b2c Fix writing of kept directories
Avoids wrapping directories in a `RenamedDataEntryWriter` since directories are not in the resource file pool which had meant the `ResourceFilePoolNameFunction` always returned null for directories.

Closes: #110
2021-01-08 09:53:39 +01:00
Eric Lafortune
97a5a9744b [PGC-0015] Added support for Java 16. 2020-12-27 22:32:36 +01:00
Eric Lafortune
4302e8ceca Added support for sealed classes, through permitted subclasses attributes. 2020-12-27 17:31:29 +01:00
Eric Lafortune
6037875b28 Added support for Java 14 and 15, including record attributes. 2020-12-27 17:30:48 +01:00
Philippe Guislain
868bf38e69 Update manual structure 2020-12-23 14:49:15 +01:00
James Hamilton
7547edaa83 Add buildDocumentation Gradle task 2020-12-22 11:46:26 +01:00
James Hamilton
8ce02565e6 Update error message wording in Gradle plugin 2020-12-17 20:35:44 +01:00
James Hamilton
c1f4ce0553 Remove Gradle plugin hard dependency on Android build tools
Changes the dependency on the Android build tools to `compileOnly` and improves the error message when using the plugin in a non-Android project.

Fixes: #66
2020-12-15 11:44:34 +01:00
James Hamilton
1213768d33 Add gradle wrapper to android-plugin example 2020-12-14 23:11:47 +01:00
James Hamilton
a2eb7f8f7a Fix syntax errors in gradle examples 2020-12-10 20:46:34 +01:00
James Hamilton
0cb79d8424 Add building instructions to README 2020-12-09 23:26:43 +01:00
James Hamilton
4db1c9c37b Update ProGuardCORE stylisation in README 2020-12-09 22:59:36 +01:00
James Hamilton
447f397b55 Add missing Gson notice 2020-12-08 16:12:07 +01:00
James Hamilton
03172c0041 Add missing newline at end of gradle.properties file 2020-12-08 15:35:24 +01:00
James Hamilton
f8443ff522 Add missing newline at end of build.gradle file 2020-12-08 15:30:54 +01:00
James Hamilton
0b89e1c148 Fix missing Kotlin marking and stripping support
* take into account Kotlin references when reducing library classpool
* marking support
  * automatically keep `kotlin.Metadata` if `-keepkotlinmetadata`
  * unmark certain known cases where optimization causes problems
* stripping of annotations where they are not explicitly keep
2020-12-08 14:21:00 +01:00
James Hamilton
eb54bfb1d9 Add Spring Boot example 2020-11-13 18:18:12 +01:00
James Hamilton
a4989a00ff Merge pull request #95 from Guardsquare/mrjameshamilton-patch-2
Remove unused WarningPrinter
2020-11-06 00:31:20 +01:00
James Hamilton
64d818d296 Remove duplicate CSS selector in manual 2020-11-05 21:36:25 +01:00
James Hamilton
dae530fe21 Remove unused WarningPrinter 2020-11-05 21:28:06 +01:00
Eric Salemi
912d149394 Bump to version 7.0.1 2020-11-04 13:59:00 +01:00
James Hamilton
6a56cc2259 Merge pull request #93 from Guardsquare/mrjameshamilton-patch-1
Add release note for Kotlin 1.4 metadata fix
2020-11-04 13:55:19 +01:00
James Hamilton
cecb95f8d0 Add release note for Kotlin 1.4 metadata fix 2020-11-04 12:17:36 +01:00
Eric Lafortune
cbbecd815a Merge remote-tracking branch 'github/master' 2020-10-23 02:19:58 +02:00
Jago Gyselinck
46b7300da5 Backport DGD-2494 2020-10-23 00:45:26 +02:00
James Hamilton
679425e5d8 Add Gradle Kotlin DSL example 2020-09-26 21:48:32 +02:00
Sander Bogaert
8532cfa2d6 Highlight README code snippets. 2020-09-16 09:59:26 +02:00
Sander Bogaert
c70af673c7 Add 'getting help' section to README with community link. 2020-09-16 09:56:07 +02:00
662 changed files with 29293 additions and 13968 deletions

View File

@@ -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
View File

@@ -4,4 +4,7 @@
.idea
build
local.properties
/lib/
/lib/
docs/html
.kotlin
.DS_Store

View File

@@ -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
View File

@@ -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 &mdash; 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).

View File

@@ -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'
}
}
}
}

View File

@@ -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'
}
}
}
}

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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);
}

View File

@@ -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'
}
}
}
}

View File

@@ -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)
{
}
}
}

View 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;
}
}

View File

@@ -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)));
}
}
}

View File

@@ -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;
}

View File

@@ -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;
/**

View File

@@ -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);
}
/**

View File

@@ -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;
}

View File

@@ -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 = "**";

View File

@@ -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)
{

View File

@@ -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)");
}
}
}
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -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,

View 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);
}
}
}

View File

@@ -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()));
}
}

View File

@@ -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") ||

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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.

View 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());
}
}

View File

@@ -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)" : ""
);
}
}
}

View 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);
}
}

View 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);
}
}
}

View File

@@ -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.");
}
}

View File

@@ -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 {}
}

View File

@@ -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
);
}
}

View File

@@ -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());
}
}

View File

@@ -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 |

View 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);
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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;
/**

View 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));
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View 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