Compare commits

..

31 Commits
v7.6 ... sync

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
201 changed files with 3395 additions and 2627 deletions

View File

@@ -15,19 +15,21 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v4
with:
path: proguard-main
- 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
with:
build-root-directory: proguard-main/
wrapper-directory: proguard-main/
arguments: test :base:testAllJavaVersions :base:jacocoTestReport jar --info
- 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@v3
if: always() # always run even if the previous step fails
uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # version 5.6.2
if: success() || failure() # always run even if the previous step fails
with:
report_paths: '**/build/test-results/test/TEST-*.xml'

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ local.properties
/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

View File

@@ -60,7 +60,6 @@ bytecode:
The resulting applications and libraries are smaller and faster.
## ❓ Getting Help
If you have **usage or general questions** please ask them in the <a href="https://community.guardsquare.com/?utm_source=github&utm_medium=site-link&utm_campaign=github-community">**Guardsquare Community**.</a>
Please use <a href="https://github.com/guardsquare/proguard/issues">**the issue tracker**</a> to report actual **bugs 🐛, crashes**, etc.
<br />
<br />
@@ -109,7 +108,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.6.0'
classpath 'com.guardsquare:proguard-gradle:7.8.0'
}
}
```
@@ -193,7 +192,7 @@ guide](CONTRIBUTING.md) if you would like to contribute.
## 📝 License
Copyright (c) 2002-2023 [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/manual/license/gplexception.md).

View File

@@ -3,19 +3,6 @@ plugins {
id 'maven-publish'
}
sourceSets.main {
java {
srcDirs = ['src']
}
resources {
srcDirs = ['src']
include '**/*.properties'
include '**/*.gif'
include '**/*.png'
include '**/*.pro'
}
}
afterEvaluate {
publishing {
publications.getByName(project.name) {

View File

@@ -10,23 +10,9 @@ repositories {
mavenCentral()
}
sourceSets.main {
java {
srcDirs = ['src']
include '**/*.java'
}
resources {
srcDirs = ['src']
include '**/*.properties'
include '**/*.gif'
include '**/*.png'
include '**/*.pro'
}
}
dependencies {
implementation project(':base')
implementation 'org.apache.ant:ant:1.9.7'
implementation 'org.apache.ant:ant:1.10.15'
}
task fatJar(type: ShadowJar) {

View File

@@ -2,10 +2,10 @@ plugins {
id 'java-library'
id 'java-test-fixtures'
id 'maven-publish'
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion"
id 'com.adarshr.test-logger' version '3.0.0'
id "org.jetbrains.kotlin.jvm"
id 'com.adarshr.test-logger' version '4.0.0'
id 'jacoco'
id "org.jlleitschuh.gradle.ktlint" version '10.2.1'
id "org.jlleitschuh.gradle.ktlint" version '12.1.2'
}
repositories {
@@ -21,17 +21,17 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
dependencies {
api "com.guardsquare:proguard-core:${proguardCoreVersion}"
implementation "com.google.code.gson:gson:${gsonVersion}"
implementation 'org.apache.logging.log4j:log4j-api:2.19.0'
implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
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.5.0-alpha07'
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.0' // for kotest framework
testImplementation 'io.kotest:kotest-assertions-core-jvm:5.9.0' // for kotest core jvm assertions
testImplementation 'io.kotest:kotest-property-jvm:5.9.0' // for kotest property test
testImplementation 'io.mockk:mockk:1.13.11' // for mocking
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'

View File

@@ -31,6 +31,7 @@ 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;
@@ -53,7 +54,6 @@ import proguard.util.kotlin.KotlinUnsupportedVersionChecker;
import proguard.util.kotlin.asserter.KotlinMetadataVerifier;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Tool for shrinking, optimizing, obfuscating, and preverifying Java classes.
@@ -220,6 +220,7 @@ public class ProGuard
configuration.obfuscate)
{
expandPrimitiveArrayConstants();
normalizeStrings();
}
if (configuration.targetClassVersion != 0)
@@ -270,6 +271,11 @@ public class ProGuard
}
}
private void normalizeStrings() throws Exception {
passRunner.run(new StringNormalizer(),appView);
}
/**
* Checks the GPL.
@@ -352,11 +358,7 @@ public class ProGuard
}
passRunner.run(new Initializer(configuration), appView);
if (configuration.keepKotlinMetadata &&
configuration.enableKotlinAsserter)
{
passRunner.run(new KotlinMetadataVerifier(configuration), appView);
}
verifyKotlinMetadata();
}
@@ -442,11 +444,7 @@ public class ProGuard
// Perform the actual shrinking.
passRunner.run(new Shrinker(configuration, afterOptimizer), appView);
if (configuration.keepKotlinMetadata &&
configuration.enableKotlinAsserter)
{
passRunner.run(new KotlinMetadataVerifier(configuration), appView);
}
verifyKotlinMetadata();
}
@@ -510,11 +508,7 @@ public class ProGuard
// Fix the Kotlin modules so the filename matches and the class names match.
passRunner.run(new NameObfuscationReferenceFixer(configuration), appView);
if (configuration.keepKotlinMetadata &&
configuration.enableKotlinAsserter)
{
passRunner.run(new KotlinMetadataVerifier(configuration), appView);
}
verifyKotlinMetadata();
}
@@ -526,6 +520,13 @@ public class ProGuard
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

View File

@@ -363,7 +363,7 @@ public class ConfigurationLogger implements Runnable
constructorParameters,
false);
if (constructorInfo != null && !isKept(constructorInfo) && (constructorParameters == null || constructorParameters.length > 0)) {
if (constructorInfo != null && !isKept(constructorInfo)) {
String signature = signatureString(INIT, constructorParameters, true);
if (shouldLog(reflectedClass, sMissingMethods, signature))
{

View File

@@ -93,7 +93,7 @@ implements DataEntryReader
if (clazz == null)
{
// The class is no longer in the classpool or encrypted class pool so it must have been shrunk.
// 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)));

View File

@@ -0,0 +1,21 @@
package proguard.normalize;
import proguard.AppView;
import proguard.classfile.visitor.ParallelAllClassVisitor;
import proguard.pass.Pass;
/**
* Ensures all strings are at most 65535 bytes in length, when encoded as modified UTF-8.
*
* @see LargeStringSplitter
*/
public class StringNormalizer implements Pass {
@Override
public void execute(AppView appView) throws Exception {
appView.programClassPool.accept(
new ParallelAllClassVisitor(
() -> new LargeStringSplitter(appView.programClassPool, appView.libraryClassPool)));
}
}

View File

@@ -78,7 +78,8 @@ implements MemberVisitor
String descriptor = member.getDescriptor(clazz);
// Check whether we're allowed to do aggressive overloading
if (!allowAggressiveOverloading)
// Annotations are always excluded from aggressive overloading due to JVM limitations
if (!allowAggressiveOverloading || clazz.extendsOrImplements(ClassConstants.NAME_JAVA_LANG_ANNOTATION_ANNOTATION))
{
// Trim the return argument from the descriptor if not.
// Works for fields and methods alike.

View File

@@ -109,7 +109,8 @@ public class MemberNameConflictFixer implements MemberVisitor
String descriptor = member.getDescriptor(clazz);
// Check whether we're allowed to overload aggressively.
if (!allowAggressiveOverloading)
// Annotations are always excluded from aggressive overloading due to JVM limitations
if (!allowAggressiveOverloading || clazz.extendsOrImplements(ClassConstants.NAME_JAVA_LANG_ANNOTATION_ANNOTATION))
{
// Trim the return argument from the descriptor if not.
// Works for fields and methods alike.

View File

@@ -81,7 +81,8 @@ implements MemberVisitor
String descriptor = member.getDescriptor(clazz);
// Check whether we're allowed to overload aggressively.
if (!allowAggressiveOverloading)
// Annotations are always excluded from aggressive overloading due to JVM limitations
if (!allowAggressiveOverloading || clazz.extendsOrImplements(ClassConstants.NAME_JAVA_LANG_ANNOTATION_ANNOTATION))
{
// Trim the return argument from the descriptor if not.
// Works for fields and methods alike.

View File

@@ -936,7 +936,11 @@ public class Optimizer implements Pass
new AllMethodVisitor(
new AllAttributeVisitor(
new DebugAttributeVisitor("Filling out fields, method parameters, and return values in synthetic classes",
new PartialEvaluator(detailedValueFactory, storingInvocationUnit, false))))));
PartialEvaluator.Builder.create()
.setValueFactory(detailedValueFactory)
.setInvocationUnit(storingInvocationUnit)
.setEvaluateAllCode(false)
.build())))));
// Evaluate non-synthetic classes. We may need to evaluate all
// casts, to account for downcasts when specializing descriptors.
@@ -959,10 +963,14 @@ public class Optimizer implements Pass
new AllMethodVisitor(
new AllAttributeVisitor(
new DebugAttributeVisitor("Filling out fields, method parameters, and return values",
new PartialEvaluator(valueFactory, storingInvocationUnit,
fieldSpecializationType ||
methodSpecializationParametertype ||
methodSpecializationReturntype)))));
PartialEvaluator.Builder.create()
.setValueFactory(valueFactory)
.setInvocationUnit(storingInvocationUnit)
.setEvaluateAllCode(
fieldSpecializationType ||
methodSpecializationParametertype ||
methodSpecializationReturntype)
.build()))));
}
};
@@ -1056,7 +1064,11 @@ public class Optimizer implements Pass
new ClassAccessFilter(AccessConstants.SYNTHETIC, 0,
new AllMethodVisitor(
new AllAttributeVisitor(
new PartialEvaluator(valueFactory, loadingInvocationUnit, false)))));
PartialEvaluator.Builder.create()
.setValueFactory(valueFactory)
.setInvocationUnit(loadingInvocationUnit)
.setEvaluateAllCode(false)
.build()))));
}
}
@@ -1083,7 +1095,11 @@ public class Optimizer implements Pass
new DebugAttributeVisitor("Simplifying code",
new OptimizationCodeAttributeFilter(
new EvaluationSimplifier(
new PartialEvaluator(valueFactory, loadingInvocationUnit, false),
PartialEvaluator.Builder.create()
.setValueFactory(valueFactory)
.setInvocationUnit(loadingInvocationUnit)
.setEvaluateAllCode(false)
.build(),
codeSimplificationAdvancedCounter,
configuration.optimizeConservatively)))));
}
@@ -1125,11 +1141,14 @@ public class Optimizer implements Pass
new OptimizationCodeAttributeFilter(
new EvaluationShrinker(
new InstructionUsageMarker(
new PartialEvaluator(referenceTracingValueFactory,
new ParameterTracingInvocationUnit(loadingInvocationUnit),
!codeSimplificationAdvanced,
referenceTracingValueFactory),
PartialEvaluator.Builder.create()
.setValueFactory(referenceTracingValueFactory)
.setInvocationUnit(new ParameterTracingInvocationUnit(loadingInvocationUnit))
.setEvaluateAllCode(!codeSimplificationAdvanced)
.setExtraInstructionVisitor(referenceTracingValueFactory)
.build(),
true, configuration.optimizeConservatively), true, deletedCounter, addedCounter)))));
}
};

View File

@@ -52,10 +52,12 @@ implements InfluenceFixpointVisitor.MemberVisitorFactory
ReferenceTracingValueFactory referenceTracingValueFactory1 =
new ReferenceTracingValueFactory(new TypedReferenceValueFactory());
PartialEvaluator partialEvaluator =
new PartialEvaluator(referenceTracingValueFactory1,
new ParameterTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory1)),
false,
referenceTracingValueFactory1);
PartialEvaluator.Builder.create()
.setValueFactory(referenceTracingValueFactory1)
.setInvocationUnit(new ParameterTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory1)))
.setEvaluateAllCode(false)
.setExtraInstructionVisitor(referenceTracingValueFactory1)
.build();
InstructionUsageMarker instructionUsageMarker =
new InstructionUsageMarker(partialEvaluator, false, false);

View File

@@ -114,7 +114,7 @@ implements AttributeVisitor,
*/
public EvaluationShrinker()
{
this(new PartialEvaluator(), true, false, null, null);
this(PartialEvaluator.Builder.create().build(), true, false, null, null);
}

View File

@@ -48,8 +48,9 @@ public class EvaluationSimplifier
implements AttributeVisitor,
InstructionVisitor
{
private static final int POS_ZERO_FLOAT_BITS = Float.floatToIntBits(0.0f);
private static final long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0);
private static final boolean ENABLE_LOWER_SLOT_REPLACEMENT = System.getProperty("optimization.enable.slot.replacement") != null;
private static final int POS_ZERO_FLOAT_BITS = Float.floatToIntBits(0.0f);
private static final long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0);
private static final Logger logger = LogManager.getLogger(EvaluationSimplifier.class);
@@ -58,7 +59,7 @@ implements AttributeVisitor,
private final PartialEvaluator partialEvaluator;
private final SideEffectInstructionChecker sideEffectInstructionChecker;
private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true);
private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true);
/**
@@ -69,7 +70,7 @@ implements AttributeVisitor,
*/
public EvaluationSimplifier(boolean predictNullPointerExceptions)
{
this(new PartialEvaluator(), null, predictNullPointerExceptions);
this(PartialEvaluator.Builder.create().build(), null, predictNullPointerExceptions);
}
@@ -692,7 +693,7 @@ implements AttributeVisitor,
replaceInstruction(clazz, offset, instruction, replacementInstruction);
}
}
else if (pushedValue.isSpecific())
else if (ENABLE_LOWER_SLOT_REPLACEMENT && pushedValue.isSpecific())
{
// Load an equivalent lower-numbered variable instead, if any.
TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
@@ -762,7 +763,7 @@ implements AttributeVisitor,
replaceInstruction(clazz, offset, instruction, replacementInstruction);
}
}
else if (pushedValue.isSpecific())
else if (ENABLE_LOWER_SLOT_REPLACEMENT && pushedValue.isSpecific())
{
// Load an equivalent lower-numbered variable instead, if any.
TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
@@ -836,7 +837,7 @@ implements AttributeVisitor,
replaceInstruction(clazz, offset, instruction, replacementInstruction);
}
}
else if (pushedValue.isSpecific())
else if (ENABLE_LOWER_SLOT_REPLACEMENT && pushedValue.isSpecific())
{
// Load an equivalent lower-numbered variable instead, if any.
TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
@@ -906,7 +907,7 @@ implements AttributeVisitor,
replaceInstruction(clazz, offset, instruction, replacementInstruction);
}
}
else if (pushedValue.isSpecific())
else if (ENABLE_LOWER_SLOT_REPLACEMENT && pushedValue.isSpecific())
{
// Load an equivalent lower-numbered variable instead, if any.
TracedVariables variables = partialEvaluator.getVariablesBefore(offset);

View File

@@ -91,7 +91,7 @@ implements AttributeVisitor
*/
public InstructionUsageMarker(boolean markExternalSideEffects)
{
this(new PartialEvaluator(), true, true, markExternalSideEffects);
this(PartialEvaluator.Builder.create().build(), true, true, markExternalSideEffects);
}
/**
@@ -129,9 +129,10 @@ implements AttributeVisitor
this.ensureSafetyForVerifier = ensureSafetyForVerifier;
this.markExternalSideEffects = markExternalSideEffects;
this.sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true, markExternalSideEffects);
if (ensureSafetyForVerifier)
{
this.simplePartialEvaluator = new PartialEvaluator(new TypedReferenceValueFactory());
if (ensureSafetyForVerifier) {
this.simplePartialEvaluator = PartialEvaluator.Builder.create()
.setValueFactory(new TypedReferenceValueFactory())
.build();
}
else
{

View File

@@ -71,7 +71,7 @@ implements ClassVisitor,
*/
public SimpleEnumUseChecker()
{
this(new PartialEvaluator(new TypedReferenceValueFactory()));
this(PartialEvaluator.Builder.create().setValueFactory(new TypedReferenceValueFactory()).build());
}

View File

@@ -72,7 +72,7 @@ implements AttributeVisitor,
*/
public SimpleEnumUseSimplifier()
{
this(new PartialEvaluator(new TypedReferenceValueFactory()), null);
this(PartialEvaluator.Builder.create().setValueFactory(new TypedReferenceValueFactory()).build(), null);
}

View File

@@ -60,11 +60,13 @@ implements InstructionVisitor
private final InstructionSequenceMatcher registerTypeAdapterFactoryMatcher;
private final InstructionSequenceMatcher serializeSpecialFloatingPointValuesMatcher;
private final TypedReferenceValueFactory valueFactory =
new TypedReferenceValueFactory();
new TypedReferenceValueFactory();
private final PartialEvaluator partialEvaluator =
new PartialEvaluator(valueFactory,
new BasicInvocationUnit(new TypedReferenceValueFactory()),
true);
PartialEvaluator.Builder.create()
.setValueFactory(valueFactory)
.setInvocationUnit(new BasicInvocationUnit(valueFactory))
.setEvaluateAllCode(true)
.build();
private final AttributeVisitor lazyPartialEvaluator =
new AttributeNameFilter(Attribute.CODE,
new SingleTimeAttributeVisitor(

View File

@@ -49,11 +49,13 @@ implements MemberVisitor,
private final CodeAttributeEditor codeAttributeEditor;
private final TypedReferenceValueFactory valueFactory =
new TypedReferenceValueFactory();
new TypedReferenceValueFactory();
private final PartialEvaluator partialEvaluator =
new PartialEvaluator(valueFactory,
new BasicInvocationUnit(new TypedReferenceValueFactory()),
true);
PartialEvaluator.Builder.create()
.setValueFactory(valueFactory)
.setInvocationUnit(new BasicInvocationUnit(valueFactory))
.setEvaluateAllCode(true)
.build();
private final AttributeVisitor lazyPartialEvaluator =
new AttributeNameFilter(Attribute.CODE,
new SingleTimeAttributeVisitor(

View File

@@ -51,12 +51,14 @@ implements InstructionVisitor
private final ClassVisitor domainClassVisitor;
private final WarningPrinter warningPrinter;
private final FromJsonInvocationMatcher[] fromJsonInvocationMatchers;
private final TypedReferenceValueFactory valueFactory =
new TypedReferenceValueFactory();
private final PartialEvaluator partialEvaluator =
new PartialEvaluator(valueFactory,
new BasicInvocationUnit(new TypedReferenceValueFactory()),
true);
private final TypedReferenceValueFactory valueFactory =
new TypedReferenceValueFactory();
private final PartialEvaluator partialEvaluator =
PartialEvaluator.Builder.create()
.setValueFactory(valueFactory)
.setInvocationUnit(new BasicInvocationUnit(valueFactory))
.setEvaluateAllCode(true)
.build();
private final AttributeVisitor lazyPartialEvaluator =
new AttributeNameFilter(Attribute.CODE,
new SingleTimeAttributeVisitor(

View File

@@ -53,11 +53,13 @@ implements InstructionVisitor
private final WarningPrinter warningPrinter;
private final ToJsonInvocationMatcher[] toJsonInvocationMatchers;
private final TypedReferenceValueFactory valueFactory =
new TypedReferenceValueFactory();
new TypedReferenceValueFactory();
private final PartialEvaluator partialEvaluator =
new PartialEvaluator(valueFactory,
new BasicInvocationUnit(new TypedReferenceValueFactory()),
true);
PartialEvaluator.Builder.create()
.setValueFactory(valueFactory)
.setInvocationUnit(new BasicInvocationUnit(valueFactory))
.setEvaluateAllCode(true)
.build();
private final AttributeVisitor lazyPartialEvaluator =
new AttributeNameFilter(Attribute.CODE,
new SingleTimeAttributeVisitor(partialEvaluator));

View File

@@ -77,10 +77,12 @@ implements AttributeVisitor,
*/
public EscapingClassMarker(ReferenceTracingValueFactory tracingValueFactory)
{
this(new PartialEvaluator(tracingValueFactory,
new ReferenceTracingInvocationUnit(new BasicInvocationUnit(tracingValueFactory)),
true,
tracingValueFactory),
this(PartialEvaluator.Builder.create()
.setValueFactory(tracingValueFactory)
.setInvocationUnit(new ReferenceTracingInvocationUnit(new BasicInvocationUnit(tracingValueFactory)))
.setEvaluateAllCode(true)
.setExtraInstructionVisitor(tracingValueFactory)
.build(),
true);
}

View File

@@ -103,7 +103,7 @@ implements MemberVisitor,
*/
public ParameterEscapeMarker(ValueFactory valueFactory, MemberVisitor extraMemberVisitor)
{
this(valueFactory, new ReferenceTracingValueFactory(valueFactory), extraMemberVisitor
this(new ReferenceTracingValueFactory(valueFactory), extraMemberVisitor
);
}
@@ -111,14 +111,15 @@ implements MemberVisitor,
/**
* Creates a new ParameterEscapeMarker.
*/
public ParameterEscapeMarker(ValueFactory valueFactory,
ReferenceTracingValueFactory tracingValueFactory,
public ParameterEscapeMarker(ReferenceTracingValueFactory tracingValueFactory,
MemberVisitor extraMemberVisitor)
{
this(new PartialEvaluator(tracingValueFactory,
new ParameterTracingInvocationUnit(new BasicInvocationUnit(tracingValueFactory)),
true,
tracingValueFactory), true, extraMemberVisitor
this(PartialEvaluator.Builder.create()
.setValueFactory(tracingValueFactory)
.setInvocationUnit(new ParameterTracingInvocationUnit(new BasicInvocationUnit(tracingValueFactory)))
.setEvaluateAllCode(true)
.setExtraInstructionVisitor(tracingValueFactory)
.build(), true, extraMemberVisitor
);
}

View File

@@ -56,13 +56,14 @@ implements ClassPoolVisitor,
private final ClassVisitor parameterEscapedMarker =
new AllMethodVisitor(
new AllAttributeVisitor(this));
private final ValueFactory valueFactory = new BasicValueFactory();
private final ReferenceTracingValueFactory tracingValueFactory = new ReferenceTracingValueFactory(valueFactory);
private final ReferenceTracingValueFactory tracingValueFactory = new ReferenceTracingValueFactory(new BasicValueFactory());
private final PartialEvaluator partialEvaluator =
new PartialEvaluator(tracingValueFactory,
new ParameterTracingInvocationUnit(new BasicInvocationUnit(tracingValueFactory)),
true,
tracingValueFactory);
PartialEvaluator.Builder.create()
.setValueFactory(tracingValueFactory)
.setInvocationUnit(new ParameterTracingInvocationUnit(new BasicInvocationUnit(tracingValueFactory)))
.setEvaluateAllCode(true)
.setExtraInstructionVisitor(tracingValueFactory)
.build();
private final ReferenceEscapeChecker referenceEscapeChecker = new ReferenceEscapeChecker(partialEvaluator, false);
// Parameters and values for visitor methods.

View File

@@ -50,7 +50,7 @@ implements MemberVisitor,
private final boolean markThisParameter;
private final boolean markAllParameters;
private final boolean analyzeCode;
private final PartialEvaluator partialEvaluator = new PartialEvaluator();
private final PartialEvaluator partialEvaluator = PartialEvaluator.Builder.create().build();
/**

View File

@@ -86,10 +86,13 @@ implements AttributeVisitor,
*/
private ReferenceEscapeChecker(ReferenceTracingValueFactory referenceTracingValueFactory)
{
this(new PartialEvaluator(referenceTracingValueFactory,
new ParameterTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory)),
true,
referenceTracingValueFactory),
this(
PartialEvaluator.Builder.create()
.setValueFactory(referenceTracingValueFactory)
.setInvocationUnit(new ParameterTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory)))
.setEvaluateAllCode(true)
.setExtraInstructionVisitor(referenceTracingValueFactory)
.build(),
true);
}

View File

@@ -1886,6 +1886,7 @@ implements ClassVisitor,
markAsUsed(kotlinPropertyMetadata.receiverType);
markAsUsed(kotlinPropertyMetadata.typeParameters);
markAsUsed(kotlinPropertyMetadata.setterParameters);
markAsUsed(kotlinPropertyMetadata.setterParameter);
markAsUsed(kotlinPropertyMetadata.type);
if (kotlinPropertyMetadata.flags.hasAnnotations &&
@@ -2068,7 +2069,10 @@ implements ClassVisitor,
else if (kotlinTypeMetadata.aliasName != null && !isUsed(kotlinTypeMetadata.referencedTypeAlias))
{
markAsUsed(kotlinTypeMetadata.referencedTypeAlias);
kotlinTypeMetadata.referencedTypeAlias.accept(null, null, this);
kotlinTypeMetadata.referencedTypeAlias.accept(
kotlinTypeMetadata.referencedTypeAlias.referencedDeclarationContainer.ownerReferencedClass,
kotlinTypeMetadata.referencedTypeAlias.referencedDeclarationContainer,
this);
}
markAsUsed(kotlinTypeMetadata.typeArguments);

View File

@@ -192,6 +192,7 @@ implements KotlinMetadataVisitor,
kotlinPropertyMetadata.setterSignature = null;
kotlinPropertyMetadata.referencedSetterMethod = null;
kotlinPropertyMetadata.flags.isVar = false;
kotlinPropertyMetadata.setterParameter = null;
kotlinPropertyMetadata.setterParameters.clear();
}

View File

@@ -66,13 +66,20 @@ public class KotlinUnsupportedVersionChecker implements Pass
@Override
public void visitUnsupportedKotlinMetadata(Clazz clazz, UnsupportedKotlinMetadata kotlinMetadata)
{
if (kotlinMetadata.mv == null || kotlinMetadata.mv.length < 3 ||
!isSupportedMetadataVersion(new KotlinMetadataVersion(kotlinMetadata.mv)))
if (kotlinMetadata.mv != null
&& (kotlinMetadata.mv.length == 2 || kotlinMetadata.mv.length == 3)
&& !isSupportedMetadataVersion(new KotlinMetadataVersion(kotlinMetadata.mv)))
{
throw new RuntimeException(
"Unsupported Kotlin metadata version found on class '" + clazz.getName() + "'." +
System.lineSeparator() +
"Kotlin versions up to " + LATEST_STABLE_SUPPORTED + " are supported.");
"Unsupported Kotlin metadata version "
+ new KotlinMetadataVersion(kotlinMetadata.mv)
+ " found on class '"
+ clazz.getName()
+ "'."
+ System.lineSeparator()
+ "Kotlin versions up to "
+ LATEST_STABLE_SUPPORTED
+ " are supported.");
}
else
{

View File

@@ -35,20 +35,23 @@ class AfterInitConfigurationVerifierTest : FreeSpec({
"Given a configuration with target specified" - {
val configuration = """
val configuration =
"""
-verbose
-target 6
""".trimIndent().asConfiguration()
""".trimIndent().asConfiguration()
"It should throw an exception if program class pool contains a class with class file version > jdk 11" {
val view = AppView()
view.programClassPool.addClass(FakeClass(CLASS_VERSION_12))
val exception = shouldThrow<RuntimeException> {
AfterInitConfigurationVerifier(configuration).execute(view)
}
val exception =
shouldThrow<RuntimeException> {
AfterInitConfigurationVerifier(configuration).execute(view)
}
exception.message shouldContain "-target can only be used with class file versions <= 55 (Java 11)."
exception.message shouldContain "The input classes contain version 56 class files which cannot be backported to target version (50)."
exception.message shouldContain "The input classes contain version 56 class files which cannot be backported " +
"to target version (50)."
}
"It should not throw an exception if program class pool contains classes with class file version = jdk 11" {
@@ -72,9 +75,10 @@ class AfterInitConfigurationVerifierTest : FreeSpec({
"Given a configuration with a target specified and classes above Java 11" - {
val configuration = """
val configuration =
"""
-target 17
""".trimIndent().asConfiguration()
""".trimIndent().asConfiguration()
"It should not throw an exception if -target is set but all the classes are already at the targeted version (no backport needed)" {
val view = AppView()
@@ -93,33 +97,38 @@ class AfterInitConfigurationVerifierTest : FreeSpec({
addClass(FakeClass(CLASS_VERSION_17))
}
val output = getLogOutputOf {
AfterInitConfigurationVerifier(configuration).execute(view)
}
val output =
getLogOutputOf {
AfterInitConfigurationVerifier(configuration).execute(view)
}
output shouldContain "-target is deprecated when using class file above"
}
"It should throw an exception if -target is set and the version of one of the classes version is different from the target version" {
val view = AppView()
with(view.programClassPool) {
addClass(FakeClass(CLASS_VERSION_18))
addClass(FakeClass(CLASS_VERSION_17))
}
"It should throw an exception if -target is set and the version of one of the classes version is different from" +
"the target version" {
val view = AppView()
with(view.programClassPool) {
addClass(FakeClass(CLASS_VERSION_18))
addClass(FakeClass(CLASS_VERSION_17))
}
val exception = shouldThrow<RuntimeException> {
AfterInitConfigurationVerifier(configuration).execute(view)
}
val exception =
shouldThrow<RuntimeException> {
AfterInitConfigurationVerifier(configuration).execute(view)
}
exception.message shouldContain "-target can only be used with class file versions <= 55 (Java 11)."
exception.message shouldContain "The input classes contain version 62 class files which cannot be backported to target version (61)."
}
exception.message shouldContain "-target can only be used with class file versions <= 55 (Java 11)."
exception.message shouldContain "The input classes contain version 62 class files which cannot be backported " +
"to target version (61)."
}
}
"Given a configuration with no target specified" - {
val configuration = """
val configuration =
"""
-verbose
""".trimIndent().asConfiguration()
""".trimIndent().asConfiguration()
"It should not throw an exception if program class pool contains classes with class file version > jdk 11" {
val view = AppView()

View File

@@ -20,14 +20,15 @@ import proguard.testutils.ClassPoolBuilder
import proguard.testutils.JavaSource
class ClassMergerTest : FreeSpec({
val classBPools = ClassPoolBuilder.fromSource(
JavaSource(
"B.java",
"""
public class B {}
""".trimIndent()
val classBPools =
ClassPoolBuilder.fromSource(
JavaSource(
"B.java",
"""
public class B {}
""".trimIndent(),
),
)
)
val classB = classBPools.programClassPool.getClass("B") as ProgramClass
classB.accept(ClassVersionSetter(CLASS_VERSION_1_8))
@@ -35,18 +36,19 @@ class ClassMergerTest : FreeSpec({
classBPools.programClassPool.classesAccept(ProcessingInfoSetter(ProgramClassOptimizationInfo()))
"Given a non-nested class" - {
val classAPools = ClassPoolBuilder.fromSource(
AssemblerSource(
"A.jbc",
"""
version 1.8;
public class A extends java.lang.Object [
SourceFile "A.java";
] {
}
""".trimIndent()
val classAPools =
ClassPoolBuilder.fromSource(
AssemblerSource(
"A.jbc",
"""
version 1.8;
public class A extends java.lang.Object [
SourceFile "A.java";
] {
}
""".trimIndent(),
),
)
)
val classA = classAPools.programClassPool.getClass("A") as ProgramClass
"the check should indicate so" {
@@ -60,19 +62,20 @@ class ClassMergerTest : FreeSpec({
}
"Given a nested class" - {
val classAPools = ClassPoolBuilder.fromSource(
AssemblerSource(
"A.jbc",
"""
version 1.8;
public class A extends java.lang.Object [
NestHost java.lang.Class;
SourceFile "A.java";
] {
}
""".trimIndent()
val classAPools =
ClassPoolBuilder.fromSource(
AssemblerSource(
"A.jbc",
"""
version 1.8;
public class A extends java.lang.Object [
NestHost java.lang.Class;
SourceFile "A.java";
] {
}
""".trimIndent(),
),
)
)
val classA = classAPools.programClassPool.getClass("A") as ProgramClass
@@ -87,26 +90,27 @@ class ClassMergerTest : FreeSpec({
}
"Given a class with no native methods" - {
val targetPools = ClassPoolBuilder.fromSource(
JavaSource(
"Target.java",
"""
public class Target {
private boolean value;
public Target(boolean value)
{
this.value = value;
}
public void setValue(boolean value)
{
this.value = value;
}
}
""".trimIndent()
val targetPools =
ClassPoolBuilder.fromSource(
JavaSource(
"Target.java",
"""
public class Target {
private boolean value;
public Target(boolean value)
{
this.value = value;
}
public void setValue(boolean value)
{
this.value = value;
}
}
""".trimIndent(),
),
)
)
targetPools.libraryClassPool.classesAccept(ProcessingInfoSetter(ClassOptimizationInfo()))
targetPools.programClassPool.classesAccept(ProcessingInfoSetter(ProgramClassOptimizationInfo()))
@@ -116,21 +120,22 @@ class ClassMergerTest : FreeSpec({
val merger = ClassMerger(target, true, true, true)
"When merging in a class with no native methods" - {
val sourcePools = ClassPoolBuilder.fromSource(
JavaSource(
"Source.java",
"""
public class Source {
private String name;
public String getName()
{
return name;
}
}
""".trimIndent()
val sourcePools =
ClassPoolBuilder.fromSource(
JavaSource(
"Source.java",
"""
public class Source {
private String name;
public String getName()
{
return name;
}
}
""".trimIndent(),
),
)
)
"Then it should be possible to merge the classes" {
sourcePools.libraryClassPool.classesAccept(ProcessingInfoSetter(ClassOptimizationInfo()))
@@ -140,23 +145,24 @@ class ClassMergerTest : FreeSpec({
}
"When merging in a class with native methods" - {
val sourcePools = ClassPoolBuilder.fromSource(
JavaSource(
"Source.java",
"""
public class Source {
private String name;
private native void method();
public String getName()
{
return name;
}
}
""".trimIndent()
val sourcePools =
ClassPoolBuilder.fromSource(
JavaSource(
"Source.java",
"""
public class Source {
private String name;
private native void method();
public String getName()
{
return name;
}
}
""".trimIndent(),
),
)
)
"Then it should not be possible to merge the classes" {
sourcePools.libraryClassPool.classesAccept(ProcessingInfoSetter(ClassOptimizationInfo()))
@@ -168,28 +174,29 @@ class ClassMergerTest : FreeSpec({
"Given a class with native methods" - {
// Same as above but checking if merging in a class with native methods is still possible.
val targetPools = ClassPoolBuilder.fromSource(
JavaSource(
"Target.java",
"""
public class Target {
private boolean value;
public static native void foo();
public Target(boolean value)
{
this.value = value;
}
public void setValue(boolean value)
{
this.value = value;
}
}
""".trimIndent()
val targetPools =
ClassPoolBuilder.fromSource(
JavaSource(
"Target.java",
"""
public class Target {
private boolean value;
public static native void foo();
public Target(boolean value)
{
this.value = value;
}
public void setValue(boolean value)
{
this.value = value;
}
}
""".trimIndent(),
),
)
)
targetPools.libraryClassPool.classesAccept(ProcessingInfoSetter(ClassOptimizationInfo()))
targetPools.programClassPool.classesAccept(ProcessingInfoSetter(ProgramClassOptimizationInfo()))
@@ -199,21 +206,22 @@ class ClassMergerTest : FreeSpec({
val merger = ClassMerger(target, true, true, true)
"When merging in a class with no native methods" - {
val sourcePools = ClassPoolBuilder.fromSource(
JavaSource(
"Source.java",
"""
public class Source {
private String name;
public String getName()
{
return name;
}
}
""".trimIndent()
val sourcePools =
ClassPoolBuilder.fromSource(
JavaSource(
"Source.java",
"""
public class Source {
private String name;
public String getName()
{
return name;
}
}
""".trimIndent(),
),
)
)
"Then it should be possible to merge the classes" {
sourcePools.libraryClassPool.classesAccept(ProcessingInfoSetter(ClassOptimizationInfo()))
@@ -223,23 +231,24 @@ class ClassMergerTest : FreeSpec({
}
"When merging in a class with native methods" - {
val sourcePools = ClassPoolBuilder.fromSource(
JavaSource(
"Source.java",
"""
public class Source {
private String name;
private native void method();
public String getName()
{
return name;
}
}
""".trimIndent()
val sourcePools =
ClassPoolBuilder.fromSource(
JavaSource(
"Source.java",
"""
public class Source {
private String name;
private native void method();
public String getName()
{
return name;
}
}
""".trimIndent(),
),
)
)
"Then it should not be possible to merge the classes" {
sourcePools.libraryClassPool.classesAccept(ProcessingInfoSetter(ClassOptimizationInfo()))

View File

@@ -22,15 +22,17 @@ import testutils.asConfiguration
class ClassSpecificationVisitorFactoryTest : FreeSpec({
"Given a class specification with an extending class" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource("Foo.java", "class Foo extends Bar3 { }"),
JavaSource("Bar3.java", "class Bar3 { }")
)
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource("Foo.java", "class Foo extends Bar3 { }"),
JavaSource("Bar3.java", "class Bar3 { }"),
)
val spec = "-keep class F** extends Bar* { public *; }".asConfiguration().keep.first()
val classVisitor = spyk<ClassVisitor>()
val visitor = KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null
)
val visitor =
KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null,
)
"Then the visitor should visit the sub-class" {
programClassPool.accept(visitor)
verify(exactly = 1) {
@@ -40,15 +42,17 @@ class ClassSpecificationVisitorFactoryTest : FreeSpec({
}
"Given a class specification with an implementing class" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource("Foo.java", "class Foo implements Bar3 { }"),
JavaSource("Bar3.java", "interface Bar3 { }")
)
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource("Foo.java", "class Foo implements Bar3 { }"),
JavaSource("Bar3.java", "interface Bar3 { }"),
)
val spec = "-keep class F** implements Bar* { public *; }".asConfiguration().keep.first()
val classVisitor = spyk<ClassVisitor>()
val visitor = KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null
)
val visitor =
KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null,
)
"Then the visitor should visit the implementing class" {
programClassPool.accept(visitor)
verify(exactly = 1) {
@@ -58,15 +62,17 @@ class ClassSpecificationVisitorFactoryTest : FreeSpec({
}
"Given a class specification with a negation" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource("Foo.java", "class Foo implements Bar3 { }"),
JavaSource("Bar3.java", "interface Bar3 { }")
)
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource("Foo.java", "class Foo implements Bar3 { }"),
JavaSource("Bar3.java", "interface Bar3 { }"),
)
val spec = "-keep !interface B** { public *; }".asConfiguration().keep.first()
val classVisitor = spyk<ClassVisitor>()
val visitor = KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null
)
val visitor =
KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null,
)
"Then the visitor should not visit the interface of which the name starts with 'B'" {
programClassPool.accept(visitor)
verify(exactly = 0) {
@@ -76,15 +82,17 @@ class ClassSpecificationVisitorFactoryTest : FreeSpec({
}
"Given a conditional class specification containing a wildcard" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource("FooBar.java", "class FooBar extends Bar { }"),
JavaSource("Bar.java", "class Bar { }")
)
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource("FooBar.java", "class FooBar extends Bar { }"),
JavaSource("Bar.java", "class Bar { }"),
)
val spec = "-if class * -keep class <1> { public *; }".asConfiguration().keep.first()
val classVisitor = spyk<ClassVisitor>()
val visitor = KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null
)
val visitor =
KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null,
)
"Then the visitor should visit the matched class" {
programClassPool.accept(visitor)
verify(exactly = 1) {
@@ -94,16 +102,18 @@ class ClassSpecificationVisitorFactoryTest : FreeSpec({
}
"Given a class specification extending an annotation class" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource("Annotation.java", "@interface Annotation { }"),
JavaSource("FooBar.java", "class FooBar extends Bar { }"),
JavaSource("Bar.java", "@Annotation class Bar { }")
)
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource("Annotation.java", "@interface Annotation { }"),
JavaSource("FooBar.java", "class FooBar extends Bar { }"),
JavaSource("Bar.java", "@Annotation class Bar { }"),
)
val spec = "-keep class * extends @Annotation *".asConfiguration().keep.first()
val classVisitor = spyk<ClassVisitor>()
val visitor = KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null
)
val visitor =
KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
spec, classVisitor, null, null, null,
)
"Then the visitor should visit the matched class" {
programClassPool.accept(visitor)
verify(exactly = 1) {
@@ -113,28 +123,32 @@ class ClassSpecificationVisitorFactoryTest : FreeSpec({
}
"Given a class with fields of different types" - {
fun createFieldVisitor(config: String, fieldVisitor: MemberVisitor): ClassPoolVisitor =
fun createFieldVisitor(
config: String,
fieldVisitor: MemberVisitor,
): ClassPoolVisitor =
KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
config.asConfiguration().keep.first(), null, fieldVisitor, null, null
config.asConfiguration().keep.first(), null, fieldVisitor, null, null,
)
val (programClassPool, _) = ClassPoolBuilder.fromSource(
AssemblerSource(
"ClassWithFields.jbc",
"""
public class ClassWithFields {
java.lang.String myField;
int myField;
long myField;
com.example.ClassInAPackage myField;
java.lang.String[] myField;
int[] myField;
com.example.ClassInAPackage[] myField;
ClassWithFields myField;
}
""".trimIndent()
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
AssemblerSource(
"ClassWithFields.jbc",
"""
public class ClassWithFields {
java.lang.String myField;
int myField;
long myField;
com.example.ClassInAPackage myField;
java.lang.String[] myField;
int[] myField;
com.example.ClassInAPackage[] myField;
ClassWithFields myField;
}
""".trimIndent(),
),
)
)
"Then * should visit any non-primitive, non-array without package separator type fields" {
with(MemberCounter()) {
@@ -166,28 +180,32 @@ class ClassSpecificationVisitorFactoryTest : FreeSpec({
}
"Given a class with methods of different types" - {
fun createMethodVisitor(config: String, methodVisitor: MemberVisitor): ClassPoolVisitor =
fun createMethodVisitor(
config: String,
methodVisitor: MemberVisitor,
): ClassPoolVisitor =
KeepClassSpecificationVisitorFactory(true, false, false).createClassPoolVisitor(
config.asConfiguration().keep.first(), null, null, methodVisitor, null
config.asConfiguration().keep.first(), null, null, methodVisitor, null,
)
val (programClassPool, _) = ClassPoolBuilder.fromSource(
AssemblerSource(
"ClassWithFields.jbc",
"""
public class ClassWithFields {
public void myMethod(java.lang.String);
public void myMethod(int);
public void myMethod(long);
public void myMethod(com.example.ClassInAPackage);
public void myMethod(java.lang.String[]);
public void myMethod(int[]);
public void myMethod(com.example.ClassInAPackage[]);
public void myMethod(ClassWithFields);
}
""".trimIndent()
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
AssemblerSource(
"ClassWithFields.jbc",
"""
public class ClassWithFields {
public void myMethod(java.lang.String);
public void myMethod(int);
public void myMethod(long);
public void myMethod(com.example.ClassInAPackage);
public void myMethod(java.lang.String[]);
public void myMethod(int[]);
public void myMethod(com.example.ClassInAPackage[]);
public void myMethod(ClassWithFields);
}
""".trimIndent(),
),
)
)
"Then * should visit any non-primitive, non-array without package separator type methods" {
with(MemberCounter()) {

View File

@@ -43,8 +43,7 @@ class ConfigurationParserTest : FreeSpec({
return configuration
}
fun parseRulesAsArguments(rules: String) =
rules.split(' ', '\n').toTypedArray()
fun parseRulesAsArguments(rules: String) = rules.split(' ', '\n').toTypedArray()
"Keep rule tests" - {
"Keep rule with <fields> wildcard should be valid" {
@@ -151,12 +150,12 @@ class ConfigurationParserTest : FreeSpec({
"""-alwaysinline class * {
@org.chromium.build.annotations.AlwaysInline *;
}
"""
""",
)
"The option prints out a warning" {
customOutputStream.toString() shouldContain "Warning: The R8 option -alwaysinline is currently not supported by ProGuard.\n" +
"This option will have no effect on the optimized artifact."
customOutputStream.toString() shouldContain "Warning: The R8 option -alwaysinline is currently not " +
"supported by ProGuard.\nThis option will have no effect on the optimized artifact."
System.setOut(savedPrintStream)
}
}
@@ -191,12 +190,12 @@ class ConfigurationParserTest : FreeSpec({
"""-identifiernamestring class * {
@org.chromium.build.annotations.IdentifierNameString *;
}
"""
""",
)
"The option prints out a warning" {
customOutputStream.toString() shouldContain "Warning: The R8 option -identifiernamestring is currently not supported by ProGuard.\n" +
"This option will have no effect on the optimized artifact."
customOutputStream.toString() shouldContain "Warning: The R8 option -identifiernamestring is currently " +
"not supported by ProGuard.\nThis option will have no effect on the optimized artifact."
System.setOut(savedPrintStream)
}
}
@@ -229,8 +228,8 @@ class ConfigurationParserTest : FreeSpec({
parseConfiguration("-maximumremovedandroidloglevel 1")
"The option prints out a warning" {
customOutputStream.toString() shouldContain "Warning: The R8 option -maximumremovedandroidloglevel is currently not supported by ProGuard.\n" +
"This option will have no effect on the optimized artifact."
customOutputStream.toString() shouldContain "Warning: The R8 option -maximumremovedandroidloglevel is " +
"currently not supported by ProGuard.\nThis option will have no effect on the optimized artifact."
System.setOut(savedPrintStream)
}
}
@@ -242,15 +241,15 @@ class ConfigurationParserTest : FreeSpec({
parseConfiguration(
"""
-maximumremovedandroidloglevel 1 @org.chromium.build.annotations.DoNotStripLogs class ** {
<methods>;
}
-maximumremovedandroidloglevel 1 @org.chromium.build.annotations.DoNotStripLogs class ** {
<methods>;
}
""".trimIndent(),
)
"The option prints out a warning" {
customOutputStream.toString() shouldContain "Warning: The R8 option -maximumremovedandroidloglevel is currently not supported by ProGuard.\n" +
"This option will have no effect on the optimized artifact."
customOutputStream.toString() shouldContain "Warning: The R8 option -maximumremovedandroidloglevel is " +
"currently not supported by ProGuard.\nThis option will have no effect on the optimized artifact."
System.setOut(savedPrintStream)
}
}
@@ -260,7 +259,7 @@ class ConfigurationParserTest : FreeSpec({
class TestConfig(
val configOption: String,
classSpecificationConfig: String,
private val classSpecificationGetter: Configuration.() -> List<ClassSpecification>?
private val classSpecificationGetter: Configuration.() -> List<ClassSpecification>?,
) {
private val configuration: Configuration by lazy {
"$configOption $classSpecificationConfig".asConfiguration()
@@ -268,14 +267,15 @@ class ConfigurationParserTest : FreeSpec({
val classSpecifications: List<ClassSpecification>? get() = classSpecificationGetter.invoke(configuration)
}
fun generateTestCases(clSpec: String): List<TestConfig> = listOf(
TestConfig("-keep", clSpec) { keep },
TestConfig("-assumenosideeffects", clSpec) { assumeNoSideEffects },
TestConfig("-assumenoexternalsideeffects", clSpec) { assumeNoExternalSideEffects },
TestConfig("-assumenoescapingparameters", clSpec) { assumeNoEscapingParameters },
TestConfig("-assumenoexternalreturnvalues", clSpec) { assumeNoExternalReturnValues },
TestConfig("-assumevalues", clSpec) { assumeValues },
)
fun generateTestCases(clSpec: String): List<TestConfig> =
listOf(
TestConfig("-keep", clSpec) { keep },
TestConfig("-assumenosideeffects", clSpec) { assumeNoSideEffects },
TestConfig("-assumenoexternalsideeffects", clSpec) { assumeNoExternalSideEffects },
TestConfig("-assumenoescapingparameters", clSpec) { assumeNoEscapingParameters },
TestConfig("-assumenoexternalreturnvalues", clSpec) { assumeNoExternalReturnValues },
TestConfig("-assumevalues", clSpec) { assumeValues },
)
"Test wildcard matches all methods and fields" {
val testConfigurations = generateTestCases("class Foo { *; }") + generateTestCases("class Foo { <fields>; <methods>; }")
@@ -438,7 +438,8 @@ class ConfigurationParserTest : FreeSpec({
"Class specification with unicode identifiers" - {
"Given some -keep rules with class specifications containing supported characters for DEX file" - {
val rules = """
val rules =
"""
-keep class uu.☱ { *; }
-keep class uu.o { ** ☱; }
-keep class uu.o { *** ☱(); }
@@ -456,7 +457,7 @@ class ConfigurationParserTest : FreeSpec({
-keep class <1> { ** 𐀀*(); }
-keep class * extends 􏿿
-keep class ** implements ☱
""".trimIndent()
""".trimIndent()
val reader = ArgumentWordReader(parseRulesAsArguments(rules), null)
mockkObject(reader)
@@ -473,7 +474,8 @@ class ConfigurationParserTest : FreeSpec({
}
"Given some -keep rules with class specifications containing unsupported identifier for DEX file" - {
val rules = """
val rules =
"""
-keep class uu.${String(Character.toChars(0x00a1 - 1))} { *; }
-keep class uu.o { ** ${String(Character.toChars(0x1fff + 1))}; }
-keep class uu.o { *** ${String(Character.toChars(0x2010 - 1))}(); }
@@ -491,7 +493,7 @@ class ConfigurationParserTest : FreeSpec({
-keep class &
-keep class ;
-keep class ,
""".trimIndent()
""".trimIndent()
val reader = ArgumentWordReader(parseRulesAsArguments(rules), null)
mockkObject(reader)

View File

@@ -10,7 +10,8 @@ import java.io.StringWriter
* Test printing of the configuration (-printconfiguration option).
*/
class ConfigurationWriterTest : FreeSpec({
val EOL = System.lineSeparator()
val eol = System.lineSeparator()
fun printConfiguration(rules: String): String {
val out = StringWriter()
val configuration = Configuration()
@@ -28,22 +29,22 @@ class ConfigurationWriterTest : FreeSpec({
"Keep rules tests" - {
"Keep class constructor should be kept" {
val rules = "-keep class * {$EOL <init>();$EOL}"
val rules = "-keep class * {$eol <init>();$eol}"
val out = printConfiguration(rules)
out shouldBe rules
}
"Keep class initializer should be kept" {
val rules = "-keep class * {$EOL <clinit>();$EOL}"
val rules = "-keep class * {$eol <clinit>();$eol}"
val out = printConfiguration(rules)
val expected = "-keep class * {$EOL void <clinit>();$EOL}"
val expected = "-keep class * {$eol void <clinit>();$eol}"
out shouldBe expected
}
"Keep class initializer should respect allowobfuscation flag" {
val rules = "-keep,allowobfuscation class ** extends com.example.A {$EOL <clinit>();$EOL}"
val rules = "-keep,allowobfuscation class ** extends com.example.A {$eol <clinit>();$eol}"
val out = printConfiguration(rules)
val expected = "-keep,allowobfuscation class ** extends com.example.A {$EOL void <clinit>();$EOL}"
val expected = "-keep,allowobfuscation class ** extends com.example.A {$eol void <clinit>();$eol}"
out shouldBe expected
}
}
@@ -54,11 +55,11 @@ class ConfigurationWriterTest : FreeSpec({
}
"Comments should not be quoted" {
printConfiguration("# comment$EOL-keep class **") shouldBe "# comment$EOL-keep class **"
printConfiguration("# comment$eol-keep class **") shouldBe "# comment$eol-keep class **"
}
"Hash characters in comments should not be quoted" {
printConfiguration("# #comment$EOL-keep class **") shouldBe "# #comment$EOL-keep class **"
printConfiguration("# #comment$eol-keep class **") shouldBe "# #comment$eol-keep class **"
}
}

View File

@@ -9,26 +9,27 @@ import testutils.RequiresJavaVersion
@RequiresJavaVersion(19, 19)
class Java19RecordPatternTest : FreeSpec({
"Given a class with Java record pattern" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
public static void main(String[] args) {
printPoint(new Point(1, 2));
}
private static void printPoint(Object o) {
if (o instanceof (Point(int x, int y) p)) {
System.out.println(p + " = " + (x + y));
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
public static void main(String[] args) {
printPoint(new Point(1, 2));
}
private static void printPoint(Object o) {
if (o instanceof (Point(int x, int y) p)) {
System.out.println(p + " = " + (x + y));
}
}
}
}
record Point(int x, int y) {}
""".trimIndent()
),
javacArguments = listOf("--enable-preview", "--release", "19")
)
record Point(int x, int y) {}
""".trimIndent(),
),
javacArguments = listOf("--enable-preview", "--release", "19"),
)
"Then ProGuard should parse the class correctly" {
programClassPool.getClass("Test") shouldNotBe null

View File

@@ -9,26 +9,27 @@ import testutils.RequiresJavaVersion
@RequiresJavaVersion(20, 20)
class Java20RecordPatternTest : FreeSpec({
"Given a class with Java record pattern" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
public static void main(String[] args) {
printPoint(new Point(1, 2));
}
private static void printPoint(Object o) {
if (o instanceof Point(int x, int y)) {
System.out.println(x + y);
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
public static void main(String[] args) {
printPoint(new Point(1, 2));
}
private static void printPoint(Object o) {
if (o instanceof Point(int x, int y)) {
System.out.println(x + y);
}
}
}
}
record Point(int x, int y) {}
""".trimIndent()
),
javacArguments = listOf("--enable-preview", "--release", "20")
)
record Point(int x, int y) {}
""".trimIndent(),
),
javacArguments = listOf("--enable-preview", "--release", "20"),
)
"Then ProGuard should parse the class correctly" {
programClassPool.getClass("Test") shouldNotBe null

View File

@@ -27,22 +27,23 @@ import testutils.shouldNotHaveFlag
class MarkerTest : FreeSpec({
"Given a Kotlin inline class" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
@JvmInline
value class Password(val s: String)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
@JvmInline
value class Password(val s: String)
// The underlying JVM method descriptor will reference the
// underlying property of `Password`, rather than `Password` itself.
// The underlying JVM method descriptor will reference the
// underlying property of `Password`, rather than `Password` itself.
fun login(password: Password) {
println(password)
}
""".trimIndent()
fun login(password: Password) {
println(password)
}
""".trimIndent(),
),
)
)
beforeEach {
programClassPool.classesAccept {
@@ -51,7 +52,8 @@ class MarkerTest : FreeSpec({
}
"Then when using includedescriptorclasses modifier" - {
val config = """
val config =
"""
-keep,includedescriptorclasses class TestKt {
<methods>;
}
@@ -71,7 +73,8 @@ class MarkerTest : FreeSpec({
}
"Then when not using includedescriptorclasses modifier" - {
val config = """
val config =
"""
-keep class TestKt {
<methods>;
}
@@ -92,25 +95,27 @@ class MarkerTest : FreeSpec({
}
"Given a Kotlin interface with default method implementation in the interface itself" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
interface Test {
fun foo() {
TODO()
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
interface Test {
fun foo() {
TODO()
}
}
}
""".trimIndent()
),
kotlincArguments = listOf("-Xjvm-default=all")
)
""".trimIndent(),
),
kotlincArguments = listOf("-Xjvm-default=all"),
)
// Run the asserter to ensure any metadata that isn't initialized correctly is thrown away
KotlinMetadataVerifier(Configuration()).execute(AppView(programClassPool, libraryClassPool))
"Then when marking" - {
val config = """
val config =
"""
-keep class Test {
<methods>;
}
@@ -134,25 +139,27 @@ class MarkerTest : FreeSpec({
}
"Given a Kotlin interface with default method implementation in compatibility mode" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
interface Test {
fun foo() {
TODO()
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
interface Test {
fun foo() {
TODO()
}
}
}
""".trimIndent()
),
kotlincArguments = listOf("-Xjvm-default=all-compatibility")
)
""".trimIndent(),
),
kotlincArguments = listOf("-Xjvm-default=all-compatibility"),
)
// Run the asserter to ensure any metadata that isn't initialized correctly is thrown away
KotlinMetadataVerifier(Configuration()).execute(AppView(programClassPool, libraryClassPool))
"Then when marking" - {
val config = """
val config =
"""
-keep class Test {
<methods>;
}

View File

@@ -0,0 +1,47 @@
package proguard.obfuscate
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.equals.shouldBeEqual
import proguard.classfile.ProgramClass
import proguard.testutils.ClassPoolBuilder
import proguard.testutils.JavaSource
import java.util.HashMap
class AnnotationTest : FreeSpec({
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public @interface Test {
boolean testBoolean1() default false;
boolean testBoolean2() default true;
String testString1() default "";
}
""".trimIndent(),
),
)
val testClass = programClassPool.getClass("Test") as ProgramClass
"Annotation members should be excluded from aggressive overloading" {
val descriptorMap = HashMap<String, Map<String, String>>()
testClass.methodsAccept(
MemberObfuscator(
true,
SimpleNameFactory(),
descriptorMap,
),
)
descriptorMap shouldBeEqual
mapOf(
"()" to
mapOf(
"a" to "testBoolean1",
"b" to "testBoolean2",
"c" to "testString1",
),
)
}
})

View File

@@ -25,7 +25,11 @@ import io.kotest.matchers.collections.shouldNotBeIn
class SimpleNameFactoryTest : StringSpec({
val reservedNames = listOf("AUX", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "CON", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", "NUL", "PRN")
val reservedNames =
listOf(
"AUX", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "CON", "LPT1", "LPT2",
"LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", "NUL", "PRN",
)
"Should not generate WINDOWS reserved names with mixed case enabled" {

View File

@@ -53,15 +53,16 @@ import proguard.testutils.KotlinSource
class KotlinCallableReferenceFixerTest : FreeSpec({
"Given a function callable reference" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
fun original() = "bar"
fun ref() = ::original
""".trimIndent()
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
fun original() = "bar"
fun ref() = ::original
""".trimIndent(),
),
)
)
programClassPool.classesAccept(
MultiClassVisitor(
@@ -69,15 +70,15 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
"TestKt",
ClassRenamer(
{ "Obfuscated" },
{ clazz, member -> if (member.getName(clazz) == "original") "obfuscated" else member.getName(clazz) }
)
{ clazz, member -> if (member.getName(clazz) == "original") "obfuscated" else member.getName(clazz) },
),
),
createFixer(programClassPool, libraryClassPool)
)
createFixer(programClassPool, libraryClassPool),
),
)
val callableRefInfoVisitor = spyk<CallableReferenceInfoVisitor>()
val ownerVisitor = spyk< KotlinMetadataVisitor>()
val ownerVisitor = spyk<KotlinMetadataVisitor>()
val testVisitor = createVisitor(callableRefInfoVisitor, ownerVisitor)
programClassPool.classesAccept("TestKt\$ref\$1", testVisitor)
@@ -89,7 +90,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
it.name shouldBe "obfuscated"
it.signature shouldBe "obfuscated()Ljava/lang/String;"
it.owner shouldNotBe null
}
},
)
}
@@ -98,7 +99,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
withArg {
it.name shouldBe "Obfuscated"
},
ofType(KotlinFileFacadeKindMetadata::class)
ofType(KotlinFileFacadeKindMetadata::class),
)
}
}
@@ -108,26 +109,30 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
"TestKt\$ref\$1",
AllConstantVisitor(
object : ConstantVisitor {
override fun visitAnyConstant(clazz: Clazz, constant: Constant) {
override fun visitAnyConstant(
clazz: Clazz,
constant: Constant,
) {
constant.toString() shouldNotContainIgnoringCase "original"
}
}
)
},
),
)
}
}
"Given a non-optimized function callable reference" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
fun original() = "bar"
fun ref() = ::original
""".trimIndent()
),
kotlincArguments = listOf("-Xno-optimized-callable-references")
)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
fun original() = "bar"
fun ref() = ::original
""".trimIndent(),
),
kotlincArguments = listOf("-Xno-optimized-callable-references"),
)
programClassPool.classesAccept(
MultiClassVisitor(
@@ -135,15 +140,15 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
"TestKt",
ClassRenamer(
{ "Obfuscated" },
{ clazz, member -> if (member.getName(clazz) == "original") "obfuscated" else member.getName(clazz) }
)
{ clazz, member -> if (member.getName(clazz) == "original") "obfuscated" else member.getName(clazz) },
),
),
createFixer(programClassPool, libraryClassPool)
)
createFixer(programClassPool, libraryClassPool),
),
)
val callableRefInfoVisitor = spyk<CallableReferenceInfoVisitor>()
val ownerVisitor = spyk< KotlinMetadataVisitor>()
val ownerVisitor = spyk<KotlinMetadataVisitor>()
val testVisitor = createVisitor(callableRefInfoVisitor, ownerVisitor)
programClassPool.classesAccept("TestKt\$ref\$1", testVisitor)
@@ -155,7 +160,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
it.name shouldBe "obfuscated"
it.signature shouldBe "obfuscated()Ljava/lang/String;"
it.owner shouldNotBe null
}
},
)
}
@@ -164,7 +169,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
withArg {
it.name shouldBe "Obfuscated"
},
ofType(KotlinFileFacadeKindMetadata::class)
ofType(KotlinFileFacadeKindMetadata::class),
)
}
}
@@ -174,28 +179,32 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
"TestKt\$ref\$1",
AllConstantVisitor(
object : ConstantVisitor {
override fun visitAnyConstant(clazz: Clazz, constant: Constant) {
override fun visitAnyConstant(
clazz: Clazz,
constant: Constant,
) {
constant.toString() shouldNotContainIgnoringCase "original"
}
}
)
},
),
)
}
}
"Given a property callable reference" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Foo {
var original = "bar"
}
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Foo {
var original = "bar"
}
fun ref() = Foo()::original
""".trimIndent()
fun ref() = Foo()::original
""".trimIndent(),
),
)
)
programClassPool.classesAccept(
MultiClassVisitor(
@@ -211,22 +220,22 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
member.getName(clazz) == "setOriginal" -> "setObfuscated"
else -> member.getName(clazz)
}
}
},
),
ReferencedKotlinMetadataVisitor(
AllPropertyVisitor
{ _, _, property ->
if (property.name == "original") property.name = "obfuscated"
}
)
)
{ _, _, property ->
if (property.name == "original") property.name = "obfuscated"
},
),
),
),
createFixer(programClassPool, libraryClassPool)
)
createFixer(programClassPool, libraryClassPool),
),
)
val callableRefInfoVisitor = spyk<CallableReferenceInfoVisitor>()
val ownerVisitor = spyk< KotlinMetadataVisitor>()
val ownerVisitor = spyk<KotlinMetadataVisitor>()
val testVisitor = createVisitor(callableRefInfoVisitor, ownerVisitor)
programClassPool.classesAccept("TestKt\$ref\$1", testVisitor)
@@ -238,7 +247,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
it.name shouldBe "obfuscated"
it.signature shouldBe "getObfuscated()Ljava/lang/String;"
it.owner shouldNotBe null
}
},
)
}
@@ -247,7 +256,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
withArg {
it.name shouldBe "Obfuscated"
},
ofType(KotlinClassKindMetadata::class)
ofType(KotlinClassKindMetadata::class),
)
}
}
@@ -257,33 +266,37 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
"TestKt\$ref\$1",
AllConstantVisitor(
object : ConstantVisitor {
override fun visitAnyConstant(clazz: Clazz, constant: Constant) {
override fun visitAnyConstant(
clazz: Clazz,
constant: Constant,
) {
constant.toString() shouldNotContainIgnoringCase "original"
}
}
)
},
),
)
}
}
"Given a Java method callable reference" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
val javaClassInstance = Original()
fun ref() = javaClassInstance::original
""".trimIndent()
),
JavaSource(
"Original.java",
"""
public class Original {
public String original() { return "bar"; }
}
""".trimIndent()
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
val javaClassInstance = Original()
fun ref() = javaClassInstance::original
""".trimIndent(),
),
JavaSource(
"Original.java",
"""
public class Original {
public String original() { return "bar"; }
}
""".trimIndent(),
),
)
)
programClassPool.classesAccept(
MultiClassVisitor(
@@ -291,15 +304,15 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
"Original",
ClassRenamer(
{ "Obfuscated" },
{ clazz, member -> if (member.getName(clazz) == "original") "obfuscated" else member.getName(clazz) }
)
{ clazz, member -> if (member.getName(clazz) == "original") "obfuscated" else member.getName(clazz) },
),
),
createFixer(programClassPool, libraryClassPool)
)
createFixer(programClassPool, libraryClassPool),
),
)
val callableRefInfoVisitor = spyk<CallableReferenceInfoVisitor>()
val ownerVisitor = spyk< KotlinMetadataVisitor>()
val ownerVisitor = spyk<KotlinMetadataVisitor>()
val testVisitor = createVisitor(callableRefInfoVisitor, ownerVisitor)
programClassPool.classesAccept("TestKt\$ref\$1", testVisitor)
@@ -311,7 +324,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
it.name shouldBe "obfuscated"
it.signature shouldBe "obfuscated()Ljava/lang/String;"
it.owner shouldBe null
}
},
)
}
@@ -320,7 +333,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
withArg {
it.name shouldBe "Obfuscated"
},
ofType(KotlinMetadata::class)
ofType(KotlinMetadata::class),
)
}
}
@@ -330,33 +343,37 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
"TestKt\$ref\$1",
AllConstantVisitor(
object : ConstantVisitor {
override fun visitAnyConstant(clazz: Clazz, constant: Constant) {
override fun visitAnyConstant(
clazz: Clazz,
constant: Constant,
) {
constant.toString() shouldNotContainIgnoringCase "original"
}
}
)
},
),
)
}
}
"Given a Java field callable reference" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
val javaClassInstance = Original()
fun ref() = javaClassInstance::original
""".trimIndent()
),
JavaSource(
"Original.java",
"""
public class Original {
public String original = "bar";
}
""".trimIndent()
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
val javaClassInstance = Original()
fun ref() = javaClassInstance::original
""".trimIndent(),
),
JavaSource(
"Original.java",
"""
public class Original {
public String original = "bar";
}
""".trimIndent(),
),
)
)
programClassPool.classesAccept(
MultiClassVisitor(
@@ -364,15 +381,15 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
"Original",
ClassRenamer(
{ "Obfuscated" },
{ clazz, member -> if (member.getName(clazz) == "original") "obfuscated" else member.getName(clazz) }
)
{ clazz, member -> if (member.getName(clazz) == "original") "obfuscated" else member.getName(clazz) },
),
),
createFixer(programClassPool, libraryClassPool)
)
createFixer(programClassPool, libraryClassPool),
),
)
val callableRefInfoVisitor = spyk<CallableReferenceInfoVisitor>()
val ownerVisitor = spyk< KotlinMetadataVisitor>()
val ownerVisitor = spyk<KotlinMetadataVisitor>()
val testVisitor = createVisitor(callableRefInfoVisitor, ownerVisitor)
programClassPool.classesAccept("TestKt\$ref\$1", testVisitor)
@@ -384,7 +401,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
it.name shouldBe "obfuscated"
it.signature shouldBe "getObfuscated()Ljava/lang/String;"
it.owner shouldBe null
}
},
)
}
@@ -393,7 +410,7 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
withArg {
it.name shouldBe "Obfuscated"
},
ofType(KotlinMetadata::class)
ofType(KotlinMetadata::class),
)
}
}
@@ -403,29 +420,45 @@ class KotlinCallableReferenceFixerTest : FreeSpec({
"TestKt\$ref\$1",
AllConstantVisitor(
object : ConstantVisitor {
override fun visitAnyConstant(clazz: Clazz, constant: Constant) {
override fun visitAnyConstant(
clazz: Clazz,
constant: Constant,
) {
constant.toString() shouldNotContainIgnoringCase "original"
}
}
)
},
),
)
}
}
})
private fun createVisitor(callableRefInfoVisitor: CallableReferenceInfoVisitor, ownerVisitor: KotlinMetadataVisitor) = ReferencedKotlinMetadataVisitor(
private fun createVisitor(
callableRefInfoVisitor: CallableReferenceInfoVisitor,
ownerVisitor: KotlinMetadataVisitor,
) = ReferencedKotlinMetadataVisitor(
object : KotlinMetadataVisitor {
override fun visitAnyKotlinMetadata(clazz: Clazz, kotlinMetadata: KotlinMetadata) {}
override fun visitKotlinSyntheticClassMetadata(clazz: Clazz, classMetadata: KotlinSyntheticClassKindMetadata) {
override fun visitAnyKotlinMetadata(
clazz: Clazz,
kotlinMetadata: KotlinMetadata,
) {}
override fun visitKotlinSyntheticClassMetadata(
clazz: Clazz,
classMetadata: KotlinSyntheticClassKindMetadata,
) {
classMetadata.callableReferenceInfoAccept(callableRefInfoVisitor)
classMetadata.callableReferenceInfoAccept { it.ownerAccept(ownerVisitor) }
}
}
},
)
private fun createFixer(programClassPool: ClassPool, libraryClassPool: ClassPool) = MultiClassVisitor(
private fun createFixer(
programClassPool: ClassPool,
libraryClassPool: ClassPool,
) = MultiClassVisitor(
MemberReferenceFixer(false),
ClassReferenceFixer(true),
ReferencedKotlinMetadataVisitor(KotlinCallableReferenceFixer(programClassPool, libraryClassPool)),
ConstantPoolShrinker()
ConstantPoolShrinker(),
)

View File

@@ -33,22 +33,24 @@ import proguard.util.ProcessingFlags
class KotlinIntrinsicsReplacementSequencesTest : FreeSpec({
"Given a class with a lateinit variable" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
lateinit var lateVar : String
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
lateinit var lateVar : String
""",
),
)
)
val builder = InstructionSequenceBuilder(programClassPool, libraryClassPool)
val intrinsicsSequence = builder.ldc_(X)
.invokestatic(
KOTLIN_INTRINSICS_CLASS,
"throwUninitializedPropertyAccessException",
"(Ljava/lang/String;)V"
).instructions()
val intrinsicsSequence =
builder.ldc_(X)
.invokestatic(
KOTLIN_INTRINSICS_CLASS,
"throwUninitializedPropertyAccessException",
"(Ljava/lang/String;)V",
).instructions()
val constants = builder.constants()
val intrinsicsSequenceMatcher = InstructionSequenceMatcher(constants, intrinsicsSequence)
@@ -60,35 +62,43 @@ class KotlinIntrinsicsReplacementSequencesTest : FreeSpec({
true,
AllInstructionVisitor(
object : InstructionVisitor {
override fun visitAnyInstruction(clazz: Clazz, method: Method, codeAttribute: CodeAttribute, offset: Int, instruction: Instruction) {
override fun visitAnyInstruction(
clazz: Clazz,
method: Method,
codeAttribute: CodeAttribute,
offset: Int,
instruction: Instruction,
) {
instruction.accept(clazz, method, codeAttribute, offset, intrinsicsSequenceMatcher)
if (intrinsicsSequenceMatcher.isMatching) hasMatched = true
}
}
)
)
},
),
),
)
hasMatched shouldBe true
}
}
"Given a class with a lateinit variable without the DONT_OBFUSCATE flag" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
lateinit var lateVar : String
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
lateinit var lateVar : String
""",
),
)
)
val builder = InstructionSequenceBuilder(programClassPool, libraryClassPool)
val intrinsicsSequence = builder.ldc_(X)
.invokestatic(
KOTLIN_INTRINSICS_CLASS,
"throwUninitializedPropertyAccessException",
"(Ljava/lang/String;)V"
).instructions()
val intrinsicsSequence =
builder.ldc_(X)
.invokestatic(
KOTLIN_INTRINSICS_CLASS,
"throwUninitializedPropertyAccessException",
"(Ljava/lang/String;)V",
).instructions()
val constants = builder.constants()
val intrinsicsSequenceMatcher = InstructionSequenceMatcher(constants, intrinsicsSequence)
@@ -102,24 +112,33 @@ class KotlinIntrinsicsReplacementSequencesTest : FreeSpec({
true,
AllInstructionVisitor(
object : InstructionVisitor {
override fun visitAnyInstruction(clazz: Clazz, method: Method, codeAttribute: CodeAttribute, offset: Int, instruction: Instruction) {
override fun visitAnyInstruction(
clazz: Clazz,
method: Method,
codeAttribute: CodeAttribute,
offset: Int,
instruction: Instruction,
) {
instruction.accept(clazz, method, codeAttribute, offset, intrinsicsSequenceMatcher)
if (intrinsicsSequenceMatcher.isMatching) {
var constantIndex = intrinsicsSequenceMatcher.matchedConstantIndex(X)
clazz.constantPoolEntryAccept(
constantIndex,
object : ConstantVisitor {
override fun visitStringConstant(clazz: Clazz, stringConstant: StringConstant) {
override fun visitStringConstant(
clazz: Clazz,
stringConstant: StringConstant,
) {
matchedConstant = stringConstant.getString(clazz)
}
}
},
)
}
}
}
)
)
)
},
),
),
),
)
matchedConstant shouldBe ""
@@ -127,22 +146,24 @@ class KotlinIntrinsicsReplacementSequencesTest : FreeSpec({
}
"Given a class with a lateinit variable and the DONT_OBFUSCATE flag" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
lateinit var lateVar : String
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
lateinit var lateVar : String
""",
),
)
)
val builder = InstructionSequenceBuilder(programClassPool, libraryClassPool)
val intrinsicsSequence = builder.ldc_(X)
.invokestatic(
KOTLIN_INTRINSICS_CLASS,
"throwUninitializedPropertyAccessException",
"(Ljava/lang/String;)V"
).instructions()
val intrinsicsSequence =
builder.ldc_(X)
.invokestatic(
KOTLIN_INTRINSICS_CLASS,
"throwUninitializedPropertyAccessException",
"(Ljava/lang/String;)V",
).instructions()
val constants = builder.constants()
val intrinsicsSequenceMatcher = InstructionSequenceMatcher(constants, intrinsicsSequence)
@@ -158,24 +179,33 @@ class KotlinIntrinsicsReplacementSequencesTest : FreeSpec({
true,
AllInstructionVisitor(
object : InstructionVisitor {
override fun visitAnyInstruction(clazz: Clazz, method: Method, codeAttribute: CodeAttribute, offset: Int, instruction: Instruction) {
override fun visitAnyInstruction(
clazz: Clazz,
method: Method,
codeAttribute: CodeAttribute,
offset: Int,
instruction: Instruction,
) {
instruction.accept(clazz, method, codeAttribute, offset, intrinsicsSequenceMatcher)
if (intrinsicsSequenceMatcher.isMatching) {
var constantIndex = intrinsicsSequenceMatcher.matchedConstantIndex(X)
clazz.constantPoolEntryAccept(
constantIndex,
object : ConstantVisitor {
override fun visitStringConstant(clazz: Clazz, stringConstant: StringConstant) {
override fun visitStringConstant(
clazz: Clazz,
stringConstant: StringConstant,
) {
matchedConstant = stringConstant.getString(clazz)
}
}
},
)
}
}
}
)
)
)
},
),
),
),
)
matchedConstant shouldBe "lateVar"

View File

@@ -48,19 +48,20 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
// InstancePerTest so the names are reset before every test
isolationMode = IsolationMode.InstancePerTest
val (programClassPool, _) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
@Suppress("UNUSED_PARAMETER")
class Foo(param1: String, param2: String, param3: String) {
var property: String = "foo"
set(param1) { }
fun foo(param1: String, param2: String, param3: String) {}
}
""".trimIndent()
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
@Suppress("UNUSED_PARAMETER")
class Foo(param1: String, param2: String, param3: String) {
var property: String = "foo"
set(param1) { }
fun foo(param1: String, param2: String, param3: String) {}
}
""".trimIndent(),
),
)
)
"Given a class with value parameters" - {
val clazz = programClassPool.getClass("Foo")
@@ -77,9 +78,9 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz.kotlinMetadataAccept(
AllConstructorVisitor(
AllValueParameterVisitor(
valueParameterVisitor
)
)
valueParameterVisitor,
),
),
)
val valueParameters = mutableListOf<ValueParameter>()
@@ -89,15 +90,16 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz,
ofType(KotlinClassKindMetadata::class),
ofType(KotlinConstructorMetadata::class),
capture(valueParameters)
capture(valueParameters),
)
}
valueParameters shouldExistInOrder listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "param1" },
{ it.parameterName == "param2" },
{ it.parameterName == "param3" }
)
valueParameters shouldExistInOrder
listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "param1" },
{ it.parameterName == "param2" },
{ it.parameterName == "param3" },
)
}
}
@@ -113,9 +115,9 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz.kotlinMetadataAccept(
AllConstructorVisitor(
AllValueParameterVisitor(
valueParameterVisitor
)
)
valueParameterVisitor,
),
),
)
val valueParameters = mutableListOf<ValueParameter>()
@@ -125,15 +127,16 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz,
ofType(KotlinClassKindMetadata::class),
ofType(KotlinConstructorMetadata::class),
capture(valueParameters)
capture(valueParameters),
)
}
valueParameters shouldExistInOrder listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "p0" },
{ it.parameterName == "p1" },
{ it.parameterName == "p2" }
)
valueParameters shouldExistInOrder
listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "p0" },
{ it.parameterName == "p1" },
{ it.parameterName == "p2" },
)
}
}
@@ -152,9 +155,9 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz.kotlinMetadataAccept(
AllConstructorVisitor(
AllValueParameterVisitor(
valueParameterVisitor
)
)
valueParameterVisitor,
),
),
)
verify(exactly = 3) {
@@ -162,15 +165,16 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz,
ofType(KotlinClassKindMetadata::class),
ofType(KotlinConstructorMetadata::class),
capture(valueParameters)
capture(valueParameters),
)
}
valueParameters shouldExistInOrder listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "p0" },
{ it.parameterName == "param2" },
{ it.parameterName == "p1" }
)
valueParameters shouldExistInOrder
listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "p0" },
{ it.parameterName == "param2" },
{ it.parameterName == "p1" },
)
}
}
}
@@ -190,9 +194,9 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz.kotlinMetadataAccept(
AllFunctionVisitor(
AllValueParameterVisitor(
valueParameterVisitor
)
)
valueParameterVisitor,
),
),
)
val valueParameters = mutableListOf<ValueParameter>()
@@ -202,15 +206,16 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz,
ofType(KotlinClassKindMetadata::class),
ofType(KotlinFunctionMetadata::class),
capture(valueParameters)
capture(valueParameters),
)
}
valueParameters shouldExistInOrder listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "param1" },
{ it.parameterName == "param2" },
{ it.parameterName == "param3" }
)
valueParameters shouldExistInOrder
listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "param1" },
{ it.parameterName == "param2" },
{ it.parameterName == "param3" },
)
}
}
@@ -226,9 +231,9 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz.kotlinMetadataAccept(
AllFunctionVisitor(
AllValueParameterVisitor(
valueParameterVisitor
)
)
valueParameterVisitor,
),
),
)
val valueParameters = mutableListOf<ValueParameter>()
@@ -238,15 +243,16 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz,
ofType(KotlinClassKindMetadata::class),
ofType(KotlinFunctionMetadata::class),
capture(valueParameters)
capture(valueParameters),
)
}
valueParameters shouldExistInOrder listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "p0" },
{ it.parameterName == "p1" },
{ it.parameterName == "p2" }
)
valueParameters shouldExistInOrder
listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "p0" },
{ it.parameterName == "p1" },
{ it.parameterName == "p2" },
)
}
}
@@ -265,9 +271,9 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz.kotlinMetadataAccept(
AllFunctionVisitor(
AllValueParameterVisitor(
valueParameterVisitor
)
)
valueParameterVisitor,
),
),
)
verify(exactly = 3) {
@@ -275,15 +281,16 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz,
ofType(KotlinClassKindMetadata::class),
ofType(KotlinFunctionMetadata::class),
capture(valueParameters)
capture(valueParameters),
)
}
valueParameters shouldExistInOrder listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "p0" },
{ it.parameterName == "param2" },
{ it.parameterName == "p1" }
)
valueParameters shouldExistInOrder
listOf<(ValueParameter) -> Boolean>(
{ it.parameterName == "p0" },
{ it.parameterName == "param2" },
{ it.parameterName == "p1" },
)
}
}
}
@@ -303,9 +310,9 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz.kotlinMetadataAccept(
AllPropertyVisitor(
AllValueParameterVisitor(
valueParameterVisitor
)
)
valueParameterVisitor,
),
),
)
verify {
@@ -313,7 +320,7 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz,
ofType(KotlinClassKindMetadata::class),
ofType(KotlinPropertyMetadata::class),
withArg { it.parameterName shouldBe "param1" }
withArg { it.parameterName shouldBe "param1" },
)
}
}
@@ -331,9 +338,9 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz.kotlinMetadataAccept(
AllPropertyVisitor(
AllValueParameterVisitor(
valueParameterVisitor
)
)
valueParameterVisitor,
),
),
)
verify {
@@ -341,7 +348,7 @@ class KotlinValueParameterNameShrinkerTest : FreeSpec({
clazz,
ofType(KotlinClassKindMetadata::class),
ofType(KotlinPropertyMetadata::class),
withArg { it.parameterName shouldBe "p0" }
withArg { it.parameterName shouldBe "p0" },
)
}
}

View File

@@ -29,37 +29,39 @@ class KotlinContextReceiverParameterShrinkingTest : StringSpec({
// with existing optimizations.
"Given a Kotlin function with ContextReceivers" {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
}
}
class Used {
fun used(message: String) { }
}
}
class Used {
fun used(message: String) { }
}
context(Used, Unused)
fun String.foo() {
used(this)
}
fun main() {
with (Unused()) { with (Used()) { "test".foo() } }
}
""".trimIndent()
),
kotlincArguments = listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions"
context(Used, Unused)
fun String.foo() {
used(this)
}
fun main() {
with (Unused()) { with (Used()) { "test".foo() } }
}
""".trimIndent(),
),
kotlincArguments =
listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions",
),
)
)
optimize(programClassPool, libraryClassPool)
@@ -72,38 +74,40 @@ class KotlinContextReceiverParameterShrinkingTest : StringSpec({
}
"Given a Kotlin function with default parameters with ContextReceivers" {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
}
}
class Used {
fun bar(message: String) { }
}
}
class Used {
fun bar(message: String) { }
}
context(Used, Unused)
fun String.foo(message: String = "test") {
println(this) // use the String receiver
bar(message)
}
context(Used, Unused)
fun String.foo(message: String = "test") {
println(this) // use the String receiver
bar(message)
}
fun main() {
with (Unused()) { with (Used()) { "test".foo() } }
}
""".trimIndent()
),
kotlincArguments = listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions"
fun main() {
with (Unused()) { with (Used()) { "test".foo() } }
}
""".trimIndent(),
),
kotlincArguments =
listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions",
),
)
)
optimize(programClassPool, libraryClassPool)
@@ -117,40 +121,42 @@ class KotlinContextReceiverParameterShrinkingTest : StringSpec({
}
"Given a Kotlin property with ContextReceivers used in the getter" {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
}
}
class Used {
fun barfo(message: String) { }
}
}
class Used {
fun barfo(message: String) { }
}
context(Used, Unused)
val String.property: String
get() {
println(this)
barfo("bar")
return "test"
}
fun main() {
with (Unused()) { with (Used()) { "test".property } }
}
""".trimIndent()
),
kotlincArguments = listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions"
context(Used, Unused)
val String.property: String
get() {
println(this)
barfo("bar")
return "test"
}
fun main() {
with (Unused()) { with (Used()) { "test".property } }
}
""".trimIndent(),
),
kotlincArguments =
listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions",
),
)
)
optimize(programClassPool, libraryClassPool)
@@ -163,44 +169,46 @@ class KotlinContextReceiverParameterShrinkingTest : StringSpec({
}
"Given a Kotlin property with a single ContextReceivers used both in the getter and setter" {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Used2 {
fun used2(message: String) {
println(message)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Used2 {
fun used2(message: String) {
println(message)
}
}
class Used {
fun used(message: String) { }
}
}
class Used {
fun used(message: String) { }
}
context(Used, Used2)
var String.property: String
set(value) {
used(value)
used2(value)
context(Used, Used2)
var String.property: String
set(value) {
used(value)
used2(value)
}
get() {
used("bar")
used2(this)
return "test"
}
fun main() {
with (Used2()) { with (Used()) { "test".property } }
}
get() {
used("bar")
used2(this)
return "test"
}
fun main() {
with (Used2()) { with (Used()) { "test".property } }
}
""".trimIndent()
),
kotlincArguments = listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions"
""".trimIndent(),
),
kotlincArguments =
listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions",
),
)
)
optimize(programClassPool, libraryClassPool)
@@ -213,41 +221,43 @@ class KotlinContextReceiverParameterShrinkingTest : StringSpec({
}
"Given a Kotlin property with a single ContextReceivers used in only the setter" {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Used2 {
fun used2(message: String) {
println(message)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Used2 {
fun used2(message: String) {
println(message)
}
}
class Used {
fun used(message: String) { }
}
}
class Used {
fun used(message: String) { }
}
context(Used, Used2)
var String.property: String
set(value) {
used2(value)
context(Used, Used2)
var String.property: String
set(value) {
used2(value)
}
get() {
return "test"
}
fun main() {
with (Used2()) { with (Used()) { "test".property } }
}
get() {
return "test"
}
fun main() {
with (Used2()) { with (Used()) { "test".property } }
}
""".trimIndent()
),
kotlincArguments = listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions"
""".trimIndent(),
),
kotlincArguments =
listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions",
),
)
)
optimize(programClassPool, libraryClassPool)
@@ -258,40 +268,42 @@ class KotlinContextReceiverParameterShrinkingTest : StringSpec({
verifyParameters(testKt, "get*,set*", p1 = "LUsed;", p2 = "LUsed2;")
}
"Given a Kotlin property with ContextReceivers used in only the getter but also containing a setter" {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
}
}
class Used {
fun used(message: String) { }
}
}
class Used {
fun used(message: String) { }
}
context(Used, Unused)
var String.property: String
set(value) { }
get() {
used(this)
return "test"
}
fun main() {
with (Unused()) { with (Used()) { "test".property } }
}
""".trimIndent()
),
kotlincArguments = listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions"
context(Used, Unused)
var String.property: String
set(value) { }
get() {
used(this)
return "test"
}
fun main() {
with (Unused()) { with (Used()) { "test".property } }
}
""".trimIndent(),
),
kotlincArguments =
listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions",
),
)
)
optimize(programClassPool, libraryClassPool)
@@ -303,30 +315,32 @@ class KotlinContextReceiverParameterShrinkingTest : StringSpec({
}
"Given a Kotlin property with ContextReceivers with no getter or setter" {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused1
class Unused2
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused1
class Unused2
class ClassWithProperty {
context(Unused1, Unused2)
val property: String by lazy { "foo" }
}
fun main() {
with (Unused2()) { with (Unused1()) { ClassWithProperty().property } }
}
""".trimIndent()
),
kotlincArguments = listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions"
class ClassWithProperty {
context(Unused1, Unused2)
val property: String by lazy { "foo" }
}
fun main() {
with (Unused2()) { with (Unused1()) { ClassWithProperty().property } }
}
""".trimIndent(),
),
kotlincArguments =
listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions",
),
)
)
val classWithProperty = programClassPool.getClass("ClassWithProperty")
@@ -339,39 +353,41 @@ class KotlinContextReceiverParameterShrinkingTest : StringSpec({
}
"Given a Kotlin class with ContextReceivers" {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
class Unused {
fun info(message: String) {
println(message)
}
}
class Used {
fun barfo(message: String) { }
}
}
class Used {
fun barfo(message: String) { }
}
context(Used, Unused)
class MyClass {
fun foo() {
barfo("test")
context(Used, Unused)
class MyClass {
fun foo() {
barfo("test")
}
}
}
fun main() {
with (Unused()) { with (Used()) { MyClass().foo() } }
}
""".trimIndent()
),
kotlincArguments = listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions"
fun main() {
with (Unused()) { with (Used()) { MyClass().foo() } }
}
""".trimIndent(),
),
kotlincArguments =
listOf(
"-Xcontext-receivers",
// Disable generation of instrinsics null checks
// In real world use-cases, -assumenosideeffects might be used
"-Xno-param-assertions",
),
)
)
optimize(programClassPool, libraryClassPool)
@@ -384,7 +400,12 @@ class KotlinContextReceiverParameterShrinkingTest : StringSpec({
}
})
private fun verifyParameters(testKt: Clazz, s: String, p1: String = "LUsed;", p2: String = "LUnused;") {
private fun verifyParameters(
testKt: Clazz,
s: String,
p1: String = "LUsed;",
p2: String = "LUnused;",
) {
val parameterVisitor = spyk<ParameterVisitor>()
testKt.methodsAccept(MemberNameFilter(s, AllParameterVisitor(false, parameterVisitor)))
verify {
@@ -398,7 +419,7 @@ private fun verifyParameters(testKt: Clazz, s: String, p1: String = "LUsed;", p2
withArg {
it shouldBe p1
},
ofType<Clazz>()
ofType<Clazz>(),
)
parameterVisitor.visitParameter(
@@ -411,13 +432,17 @@ private fun verifyParameters(testKt: Clazz, s: String, p1: String = "LUsed;", p2
withArg {
it shouldBe p2
},
ofType<Clazz>()
ofType<Clazz>(),
)
}
}
private fun optimize(programClassPool: ClassPool, libraryClassPool: ClassPool) {
val config = """
private fun optimize(
programClassPool: ClassPool,
libraryClassPool: ClassPool,
) {
val config =
"""
-keep,allowoptimization,allowshrinking class TestKt,ClassWithProperty,MyClass { *; }
-optimizations **
""".asConfiguration()

View File

@@ -44,29 +44,33 @@ class MemberDescriptorSpecializerTest : FreeSpec({
programClassPool.classesAccept(AllMemberVisitor(ProgramMemberOptimizationInfoSetter()))
// Create the optimization as in Optimizer
val fillingOutValuesClassVisitor = ClassVisitorFactory {
val valueFactory: ValueFactory = ParticularValueFactory()
val storingInvocationUnit: InvocationUnit = StoringInvocationUnit(
valueFactory,
true,
true,
true
)
ClassAccessFilter(
0, AccessConstants.SYNTHETIC,
AllMethodVisitor(
AllAttributeVisitor(
DebugAttributeVisitor(
"Filling out fields, method parameters, and return values",
PartialEvaluator(
valueFactory, storingInvocationUnit,
true
)
)
val fillingOutValuesClassVisitor =
ClassVisitorFactory {
val valueFactory: ValueFactory = ParticularValueFactory()
val storingInvocationUnit: InvocationUnit =
StoringInvocationUnit(
valueFactory,
true,
true,
true,
)
ClassAccessFilter(
0,
AccessConstants.SYNTHETIC,
AllMethodVisitor(
AllAttributeVisitor(
DebugAttributeVisitor(
"Filling out fields, method parameters, and return values",
PartialEvaluator(
valueFactory,
storingInvocationUnit,
true,
),
),
),
),
)
)
}
}
programClassPool.classesAccept(fillingOutValuesClassVisitor.createClassVisitor())
@@ -80,33 +84,34 @@ class MemberDescriptorSpecializerTest : FreeSpec({
true,
null,
null,
null
)
)
)
null,
),
),
),
)
}
"Given a method with a more general program class pool parameter type than its use" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
public static void main(String[] args) {
foo(new Foo());
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
public static void main(String[] args) {
foo(new Foo());
}
public static void foo(Bar foo) {
System.out.println(foo);
}
}
public static void foo(Bar foo) {
System.out.println(foo);
}
}
class Bar { }
class Foo extends Bar { }
""".trimIndent()
class Bar { }
class Foo extends Bar { }
""".trimIndent(),
),
)
)
"When specializing the member descriptors" - {
specializeMemberDescriptors(programClassPool, libraryClassPool)
@@ -119,12 +124,15 @@ class MemberDescriptorSpecializerTest : FreeSpec({
MemberNameFilter(
"foo*",
object : MemberVisitor {
override fun visitAnyMember(clazz: Clazz, member: Member) {
override fun visitAnyMember(
clazz: Clazz,
member: Member,
) {
memberDescriptor = member.getDescriptor(clazz)
}
}
)
)
},
),
),
)
memberDescriptor shouldBe "(LFoo;)V"
}
@@ -132,24 +140,25 @@ class MemberDescriptorSpecializerTest : FreeSpec({
}
"Given a field with a more general program class pool parameter type than its use" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
static Bar myField = null;
public static void main(String[] args) {
myField = new Foo();
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
static Bar myField = null;
public static void main(String[] args) {
myField = new Foo();
}
}
}
class Bar { }
class Foo extends Bar { }
""".trimIndent()
class Bar { }
class Foo extends Bar { }
""".trimIndent(),
),
)
)
"When specializing the member descriptors" - {
specializeMemberDescriptors(programClassPool, libraryClassPool)
@@ -162,12 +171,15 @@ class MemberDescriptorSpecializerTest : FreeSpec({
MemberNameFilter(
"myField*",
object : MemberVisitor {
override fun visitAnyMember(clazz: Clazz, member: Member) {
override fun visitAnyMember(
clazz: Clazz,
member: Member,
) {
memberDescriptor = member.getDescriptor(clazz)
}
}
)
)
},
),
),
)
memberDescriptor shouldBe "LFoo;"
}
@@ -175,20 +187,21 @@ class MemberDescriptorSpecializerTest : FreeSpec({
}
"Given a field with a more general library class pool parameter type than its use" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
static java.lang.Object myField = null;
public static void main(String[] args) {
myField = new java.lang.StringBuffer();
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
JavaSource(
"Test.java",
"""
public class Test {
static java.lang.Object myField = null;
public static void main(String[] args) {
myField = new java.lang.StringBuffer();
}
}
}
""".trimIndent()
""".trimIndent(),
),
)
)
"When specializing the member descriptors" - {
specializeMemberDescriptors(programClassPool, libraryClassPool)
@@ -201,12 +214,15 @@ class MemberDescriptorSpecializerTest : FreeSpec({
MemberNameFilter(
"myField*",
object : MemberVisitor {
override fun visitAnyMember(clazz: Clazz, member: Member) {
override fun visitAnyMember(
clazz: Clazz,
member: Member,
) {
memberDescriptor = member.getDescriptor(clazz)
}
}
)
)
},
),
),
)
// Library classes are not marked as available by default. Therefore, they are not specialized.
memberDescriptor shouldBe "Ljava/lang/Object;"

View File

@@ -9,14 +9,15 @@ import proguard.testutils.JavaSource
class SimpleEnumClassCheckerTest : FreeSpec({
"Given a enum class without instance methods" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Enum.java",
"""
public enum Enum { FOO, BAR }
""".trimIndent()
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Enum.java",
"""
public enum Enum { FOO, BAR }
""".trimIndent(),
),
)
)
"Then when checked with SimpleEnumClassChecker" - {
val enumClass = programClassPool.getClass("Enum")
@@ -30,19 +31,20 @@ class SimpleEnumClassCheckerTest : FreeSpec({
}
"Given a enum class with a public instance method" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Enum.java",
"""
public enum Enum {
FOO, BAR;
public void foo() {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Enum.java",
"""
public enum Enum {
FOO, BAR;
public void foo() {
}
}
}
""".trimIndent()
""".trimIndent(),
),
)
)
"Then when checked with SimpleEnumClassChecker" - {
val enumClass = programClassPool.getClass("Enum")
@@ -56,19 +58,20 @@ class SimpleEnumClassCheckerTest : FreeSpec({
}
"Given a enum class with a private instance method" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Enum.java",
"""
public enum Enum {
FOO, BAR;
private void foo() {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Enum.java",
"""
public enum Enum {
FOO, BAR;
private void foo() {
}
}
}
""".trimIndent()
""".trimIndent(),
),
)
)
"Then when checked with SimpleEnumClassChecker" - {
val enumClass = programClassPool.getClass("Enum")

View File

@@ -22,18 +22,19 @@ import proguard.testutils.JavaSource
class MarkedAnnotationDeleterTest : FreeSpec({
"Given a class with two annotations on its field" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource("Ann1.java", "public @interface Ann1 {}"),
JavaSource("Ann2.java", "public @interface Ann2 {}"),
JavaSource(
"A.java",
"""class A {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource("Ann1.java", "public @interface Ann1 {}"),
JavaSource("Ann2.java", "public @interface Ann2 {}"),
JavaSource(
"A.java",
"""class A {
@Ann1
@Ann2
public int a;
}"""
}""",
),
)
)
val classA = programClassPool.getClass("A")
@@ -43,15 +44,15 @@ class MarkedAnnotationDeleterTest : FreeSpec({
classA.fieldsAccept(
AllAttributeVisitor(
AllAnnotationVisitor(
ProcessingInfoSetter(mark)
)
)
ProcessingInfoSetter(mark),
),
),
)
classA.fieldsAccept(
AllAttributeVisitor(
MarkedAnnotationDeleter(mark)
)
MarkedAnnotationDeleter(mark),
),
)
"Then no annotations should remain" {
@@ -59,13 +60,17 @@ class MarkedAnnotationDeleterTest : FreeSpec({
classA.fieldsAccept(
AllAttributeVisitor(
AllAnnotationVisitor(object : AnnotationVisitor {
override fun visitAnnotation(clazz: Clazz?, annotation: Annotation?) {
count += 1
}
}
)
)
AllAnnotationVisitor(
object : AnnotationVisitor {
override fun visitAnnotation(
clazz: Clazz?,
annotation: Annotation?,
) {
count += 1
}
},
),
),
)
count shouldBe 0
@@ -74,24 +79,25 @@ class MarkedAnnotationDeleterTest : FreeSpec({
}
"Given a class with two A and B annotations on its field" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource("AnnA1.java", "public @interface AnnA1 {}"),
JavaSource("AnnA2.java", "public @interface AnnA2 {}"),
JavaSource("AnnB1.java", "public @interface AnnB1 {}"),
JavaSource("AnnB2.java", "public @interface AnnB2 {}"),
JavaSource("AnnB3.java", "public @interface AnnB3 {}"),
JavaSource(
"A.java",
"""class A {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource("AnnA1.java", "public @interface AnnA1 {}"),
JavaSource("AnnA2.java", "public @interface AnnA2 {}"),
JavaSource("AnnB1.java", "public @interface AnnB1 {}"),
JavaSource("AnnB2.java", "public @interface AnnB2 {}"),
JavaSource("AnnB3.java", "public @interface AnnB3 {}"),
JavaSource(
"A.java",
"""class A {
@AnnB1
@AnnA1
@AnnB2
@AnnA2
@AnnB3
public int a;
}"""
}""",
),
)
)
val classA = programClassPool.getClass("A")
@@ -103,16 +109,16 @@ class MarkedAnnotationDeleterTest : FreeSpec({
AllAnnotationVisitor(
AnnotationTypeFilter(
"LAnnA*;",
ProcessingInfoSetter(mark)
)
)
)
ProcessingInfoSetter(mark),
),
),
),
)
classA.fieldsAccept(
AllAttributeVisitor(
MarkedAnnotationDeleter(mark)
)
MarkedAnnotationDeleter(mark),
),
)
"Then only the B annotations should remain" {
@@ -120,13 +126,17 @@ class MarkedAnnotationDeleterTest : FreeSpec({
classA.fieldsAccept(
AllAttributeVisitor(
AllAnnotationVisitor(object : AnnotationVisitor {
override fun visitAnnotation(clazz: Clazz, annotation: Annotation) {
list.add(annotation.getType(clazz))
}
}
)
)
AllAnnotationVisitor(
object : AnnotationVisitor {
override fun visitAnnotation(
clazz: Clazz,
annotation: Annotation,
) {
list.add(annotation.getType(clazz))
}
},
),
),
)
list shouldContainExactly listOf("LAnnB1;", "LAnnB2;", "LAnnB3;")
@@ -135,16 +145,17 @@ class MarkedAnnotationDeleterTest : FreeSpec({
}
"Given a class with two annotations on its method parameter" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource("Ann1.java", "public @interface Ann1 {}"),
JavaSource("Ann2.java", "public @interface Ann2 {}"),
JavaSource(
"A.java",
"""class A {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource("Ann1.java", "public @interface Ann1 {}"),
JavaSource("Ann2.java", "public @interface Ann2 {}"),
JavaSource(
"A.java",
"""class A {
public void a(@Ann1 @Ann2 int x) {}
}"""
}""",
),
)
)
val classA = programClassPool.getClass("A")
@@ -154,15 +165,15 @@ class MarkedAnnotationDeleterTest : FreeSpec({
classA.methodsAccept(
AllAttributeVisitor(
AllAnnotationVisitor(
ProcessingInfoSetter(mark)
)
)
ProcessingInfoSetter(mark),
),
),
)
classA.methodsAccept(
AllAttributeVisitor(
MarkedAnnotationDeleter(mark)
)
MarkedAnnotationDeleter(mark),
),
)
"Then no annotations should remain" {
@@ -170,13 +181,17 @@ class MarkedAnnotationDeleterTest : FreeSpec({
classA.methodsAccept(
AllAttributeVisitor(
AllAnnotationVisitor(object : AnnotationVisitor {
override fun visitAnnotation(clazz: Clazz?, annotation: Annotation?) {
count += 1
}
}
)
)
AllAnnotationVisitor(
object : AnnotationVisitor {
override fun visitAnnotation(
clazz: Clazz?,
annotation: Annotation?,
) {
count += 1
}
},
),
),
)
count shouldBe 0
@@ -185,19 +200,20 @@ class MarkedAnnotationDeleterTest : FreeSpec({
}
"Given a class with two A and B annotations on its method parameter" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource("AnnA1.java", "public @interface AnnA1 {}"),
JavaSource("AnnA2.java", "public @interface AnnA2 {}"),
JavaSource("AnnB1.java", "public @interface AnnB1 {}"),
JavaSource("AnnB2.java", "public @interface AnnB2 {}"),
JavaSource("AnnB3.java", "public @interface AnnB3 {}"),
JavaSource(
"A.java",
"""class A {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource("AnnA1.java", "public @interface AnnA1 {}"),
JavaSource("AnnA2.java", "public @interface AnnA2 {}"),
JavaSource("AnnB1.java", "public @interface AnnB1 {}"),
JavaSource("AnnB2.java", "public @interface AnnB2 {}"),
JavaSource("AnnB3.java", "public @interface AnnB3 {}"),
JavaSource(
"A.java",
"""class A {
public void a(@AnnB1 @AnnA1 @AnnB2 @AnnA2 @AnnB3 int x) {}
}"""
}""",
),
)
)
val classA = programClassPool.getClass("A")
@@ -209,16 +225,16 @@ class MarkedAnnotationDeleterTest : FreeSpec({
AllAnnotationVisitor(
AnnotationTypeFilter(
"LAnnA*;",
ProcessingInfoSetter(mark)
)
)
)
ProcessingInfoSetter(mark),
),
),
),
)
classA.methodsAccept(
AllAttributeVisitor(
MarkedAnnotationDeleter(mark)
)
MarkedAnnotationDeleter(mark),
),
)
"Then only the B annotations should remain" {
@@ -226,13 +242,17 @@ class MarkedAnnotationDeleterTest : FreeSpec({
classA.methodsAccept(
AllAttributeVisitor(
AllAnnotationVisitor(object : AnnotationVisitor {
override fun visitAnnotation(clazz: Clazz, annotation: Annotation) {
list.add(annotation.getType(clazz))
}
}
)
)
AllAnnotationVisitor(
object : AnnotationVisitor {
override fun visitAnnotation(
clazz: Clazz,
annotation: Annotation,
) {
list.add(annotation.getType(clazz))
}
},
),
),
)
list shouldContainExactly listOf("LAnnB1;", "LAnnB2;", "LAnnB3;")

View File

@@ -22,14 +22,15 @@ import proguard.testutils.JavaSource
class TypeArgumentFinderTest : FreeSpec({
"Given an aload instruction with TypeToken" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"TypeToken.java",
"""
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
JavaSource(
"TypeToken.java",
"""
package com.google.gson.reflect;
import java.lang.reflect.Type;
public class TypeToken<T> {
Type type;
@@ -37,11 +38,11 @@ class TypeArgumentFinderTest : FreeSpec({
return type;
}
}
""".trimIndent()
),
JavaSource(
"A.java",
"""
""".trimIndent(),
),
JavaSource(
"A.java",
"""
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
@@ -51,9 +52,9 @@ class TypeArgumentFinderTest : FreeSpec({
System.out.println(x);
}
}
""".trimIndent()
""".trimIndent(),
),
)
)
"When retrieving the return type with TypeArgumentFinder" - {
val partialEvaluator = PartialEvaluator()
@@ -63,9 +64,9 @@ class TypeArgumentFinderTest : FreeSpec({
AllMethodVisitor(
MemberNameFilter(
"a",
AllAttributeVisitor(partialEvaluator)
)
)
AllAttributeVisitor(partialEvaluator),
),
),
)
val typeArgumentFinder = TypeArgumentFinder(programClassPool, libraryClassPool, partialEvaluator)
@@ -83,14 +84,14 @@ class TypeArgumentFinderTest : FreeSpec({
Instruction.OP_ALOAD_0.toInt(),
Instruction.OP_ALOAD_1.toInt(),
Instruction.OP_ALOAD_2.toInt(),
Instruction.OP_ALOAD_3.toInt()
Instruction.OP_ALOAD_3.toInt(),
),
typeArgumentFinder
)
)
)
)
)
typeArgumentFinder,
),
),
),
),
),
)
"Then we obtain the return type java.lang.String" {
@@ -100,14 +101,15 @@ class TypeArgumentFinderTest : FreeSpec({
}
"Given an invokevirtual instruction with TypeToken" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"TypeToken.java",
"""
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
JavaSource(
"TypeToken.java",
"""
package com.google.gson.reflect;
import java.lang.reflect.Type;
public class TypeToken<T> {
Type type;
@@ -115,11 +117,11 @@ class TypeArgumentFinderTest : FreeSpec({
return type;
}
}
""".trimIndent()
),
JavaSource(
"A.java",
"""
""".trimIndent(),
),
JavaSource(
"A.java",
"""
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
@@ -128,9 +130,9 @@ class TypeArgumentFinderTest : FreeSpec({
new TypeToken<String>() {}.getType();
}
}
""".trimIndent()
""".trimIndent(),
),
)
)
"When retrieving the return type with TypeArgumentFinder" - {
val typeArgumentFinder = TypeArgumentFinder(programClassPool, libraryClassPool, null)
@@ -144,12 +146,12 @@ class TypeArgumentFinderTest : FreeSpec({
AllInstructionVisitor(
InstructionOpCodeFilter(
intArrayOf(Instruction.OP_INVOKEVIRTUAL.toInt()),
typeArgumentFinder
)
)
)
)
)
typeArgumentFinder,
),
),
),
),
),
)
"Then we obtain null (Note: this case is not implemented like for aload instructions)" {
@@ -159,10 +161,11 @@ class TypeArgumentFinderTest : FreeSpec({
}
"Given an ldc instruction" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"Z.java",
"""
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
JavaSource(
"Z.java",
"""
package a.b;
public class Z {
@@ -172,9 +175,9 @@ class TypeArgumentFinderTest : FreeSpec({
public void x(Class<?> c) {}
}
""".trimIndent()
""".trimIndent(),
),
)
)
"When retrieving the return type with TypeArgumentFinder" - {
val typeArgumentFinder = TypeArgumentFinder(programClassPool, libraryClassPool, null)
@@ -188,12 +191,12 @@ class TypeArgumentFinderTest : FreeSpec({
AllInstructionVisitor(
InstructionOpCodeFilter(
intArrayOf(Instruction.OP_LDC.toInt()),
typeArgumentFinder
)
)
)
)
)
typeArgumentFinder,
),
),
),
),
),
)
"Then we obtain the return type a.b.Z" {
@@ -203,19 +206,20 @@ class TypeArgumentFinderTest : FreeSpec({
}
"Given an aload instruction" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"A.java",
"""
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
JavaSource(
"A.java",
"""
public class A {
public void a() {
String x = "text";
System.out.println(x);
}
}
""".trimIndent()
""".trimIndent(),
),
)
)
"When retrieving the return type with TypeArgumentFinder" - {
val partialEvaluator = PartialEvaluator()
@@ -225,9 +229,9 @@ class TypeArgumentFinderTest : FreeSpec({
AllMethodVisitor(
MemberNameFilter(
"a",
AllAttributeVisitor(partialEvaluator)
)
)
AllAttributeVisitor(partialEvaluator),
),
),
)
val typeArgumentFinder = TypeArgumentFinder(programClassPool, libraryClassPool, partialEvaluator)
@@ -245,14 +249,14 @@ class TypeArgumentFinderTest : FreeSpec({
Instruction.OP_ALOAD_0.toInt(),
Instruction.OP_ALOAD_1.toInt(),
Instruction.OP_ALOAD_2.toInt(),
Instruction.OP_ALOAD_3.toInt()
Instruction.OP_ALOAD_3.toInt(),
),
typeArgumentFinder
)
)
)
)
)
typeArgumentFinder,
),
),
),
),
),
)
// This is not an intended use case of the TypeArgumentFinder
@@ -264,10 +268,11 @@ class TypeArgumentFinderTest : FreeSpec({
}
"Given a new instruction" - {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
JavaSource(
"Z.java",
"""
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
JavaSource(
"Z.java",
"""
package a.b;
public class Z {
@@ -275,9 +280,9 @@ class TypeArgumentFinderTest : FreeSpec({
new Z();
}
}
""".trimIndent()
""".trimIndent(),
),
)
)
"When retrieving the return type with TypeArgumentFinder" - {
val typeArgumentFinder = TypeArgumentFinder(programClassPool, libraryClassPool, null)
@@ -291,12 +296,12 @@ class TypeArgumentFinderTest : FreeSpec({
AllInstructionVisitor(
InstructionOpCodeFilter(
intArrayOf(Instruction.OP_NEW.toInt()),
typeArgumentFinder
)
)
)
)
)
typeArgumentFinder,
),
),
),
),
),
)
"Then we obtain the return type a.b.Z" {

View File

@@ -13,19 +13,20 @@ import proguard.testutils.JavaSource
class InstantiationClassMarkerTest : FreeSpec({
"A class should be marked as instantiated" - {
"when it is instantiated with a `new` instruction" {
val (classPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Main.java",
"""
class A { }
public class Main {
public static void main(String[] args) {
System.out.println(new A());
}
}
""".trimIndent()
val (classPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Main.java",
"""
class A { }
public class Main {
public static void main(String[] args) {
System.out.println(new A());
}
}
""".trimIndent(),
),
)
)
val classA = classPool.getClass("A")
@@ -36,30 +37,31 @@ class InstantiationClassMarkerTest : FreeSpec({
AllMemberVisitor(
AllAttributeVisitor(
AllInstructionVisitor(
InstantiationClassMarker()
)
)
)
InstantiationClassMarker(),
),
),
),
)
getProgramClassOptimizationInfo(classA).isInstantiated shouldBe true
}
"when one of its subclasses is instantiated with a `new` instruction" {
val (classPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Main.java",
"""
class A { }
class B extends A { }
public class Main {
public static void main(String[] args) {
System.out.println(new B());
}
}
""".trimIndent()
val (classPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Main.java",
"""
class A { }
class B extends A { }
public class Main {
public static void main(String[] args) {
System.out.println(new B());
}
}
""".trimIndent(),
),
)
)
val classA = classPool.getClass("A")
val classB = classPool.getClass("B")
@@ -71,10 +73,10 @@ class InstantiationClassMarkerTest : FreeSpec({
AllMemberVisitor(
AllAttributeVisitor(
AllInstructionVisitor(
InstantiationClassMarker()
)
)
)
InstantiationClassMarker(),
),
),
),
)
getProgramClassOptimizationInfo(classA).isInstantiated shouldBe true

View File

@@ -23,10 +23,11 @@ class MethodInlinerJava9Test : FreeSpec({
isolationMode = IsolationMode.InstancePerTest
"Given a method calling a private method in the same interface" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""interface Foo {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""interface Foo {
default void f1() {
f2();
}
@@ -37,8 +38,8 @@ class MethodInlinerJava9Test : FreeSpec({
System.out.println(sb.toString());
}
}""",
),
)
)
val clazz = programClassPool.getClass("Foo") as ProgramClass
val method = clazz.findMethod("f1", "()V") as ProgramMethod
@@ -47,27 +48,33 @@ class MethodInlinerJava9Test : FreeSpec({
val lengthBefore = codeAttr.u4codeLength
// Initialize optimization info (used when inlining).
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter()
val optimizationInfoInitializer: ClassVisitor =
MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter(),
),
)
)
programClassPool.classesAccept(optimizationInfoInitializer)
// Create a mock method inliner which always returns true.
val methodInliner = object : MethodInliner(false, true, true) {
override fun shouldInline(clazz: Clazz?, method: Method?, codeAttribute: CodeAttribute?): Boolean = true
}
val methodInliner =
object : MethodInliner(false, true, true) {
override fun shouldInline(
clazz: Clazz?,
method: Method?,
codeAttribute: CodeAttribute?,
): Boolean = true
}
"Then the interface method is inlined" {
programClassPool.classesAccept(
AllMethodVisitor(
AllAttributeVisitor(
methodInliner
)
)
methodInliner,
),
),
)
val lengthAfter = codeAttr.u4codeLength

View File

@@ -33,10 +33,11 @@ class MethodInlinerTest : FreeSpec({
isolationMode = IsolationMode.InstancePerTest
"Given two simple functions, one calling the other" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""class Foo {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""class Foo {
static int f1() {
return f2() + 1;
}
@@ -44,9 +45,9 @@ class MethodInlinerTest : FreeSpec({
static int f2() {
return 1;
}
}"""
}""",
),
)
)
// Sanity check how the instructions look before.
val instructionsBefore = printProgramMethodInstructions(programClassPool, "Foo", "f1", "()I")
@@ -59,26 +60,32 @@ class MethodInlinerTest : FreeSpec({
"When calling the method inliner, specifying that we should always inline" - {
// Initialize optimization info (used when inlining).
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter()
val optimizationInfoInitializer: ClassVisitor =
MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter(),
),
)
)
programClassPool.classesAccept(optimizationInfoInitializer)
// Create a mock method inliner which always returns true.
val methodInliner = object : MethodInliner(false, true, true) {
override fun shouldInline(clazz: Clazz, method: Method?, codeAttribute: CodeAttribute?): Boolean = true
}
val methodInliner =
object : MethodInliner(false, true, true) {
override fun shouldInline(
clazz: Clazz,
method: Method?,
codeAttribute: CodeAttribute?,
): Boolean = true
}
programClassPool.classesAccept(
AllMethodVisitor(
AllAttributeVisitor(
methodInliner
)
)
methodInliner,
),
),
)
"Then the called function is inlined as expected" {
@@ -94,26 +101,32 @@ class MethodInlinerTest : FreeSpec({
"When calling the method inliner, specifying that we should never inline" - {
// Initialize optimization info (used when inlining).
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter()
val optimizationInfoInitializer: ClassVisitor =
MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter(),
),
)
)
programClassPool.classesAccept(optimizationInfoInitializer)
// Create a mock method inliner which always returns true.
val methodInliner = object : MethodInliner(false, true, true) {
override fun shouldInline(clazz: Clazz?, method: Method?, codeAttribute: CodeAttribute?): Boolean = false
}
val methodInliner =
object : MethodInliner(false, true, true) {
override fun shouldInline(
clazz: Clazz?,
method: Method?,
codeAttribute: CodeAttribute?,
): Boolean = false
}
programClassPool.classesAccept(
AllMethodVisitor(
AllAttributeVisitor(
methodInliner
)
)
methodInliner,
),
),
)
"Then the called function is not inlined" {
@@ -131,22 +144,23 @@ class MethodInlinerTest : FreeSpec({
"Given a function calling a big function" - {
val lotsOfPrints = (1..3000).joinToString("\n") { "System.out.println(\"${it}\");" }
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""class Foo {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""class Foo {
static void f1() {
f2();
}
static void f2() {
""" +
lotsOfPrints +
"""
lotsOfPrints +
"""
}
}"""
}""",
),
)
)
val clazz = programClassPool.getClass("Foo") as ProgramClass
val method = clazz.findMethod("f1", "()V") as ProgramMethod
@@ -156,27 +170,33 @@ class MethodInlinerTest : FreeSpec({
"When using the default maximum resulting code length parameter" - {
// Initialize optimization info (used when inlining).
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter()
val optimizationInfoInitializer: ClassVisitor =
MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter(),
),
)
)
programClassPool.classesAccept(optimizationInfoInitializer)
// Create a mock method inliner which always returns true.
val methodInliner = object : MethodInliner(false, true, true) {
override fun shouldInline(clazz: Clazz?, method: Method?, codeAttribute: CodeAttribute?): Boolean = true
}
val methodInliner =
object : MethodInliner(false, true, true) {
override fun shouldInline(
clazz: Clazz?,
method: Method?,
codeAttribute: CodeAttribute?,
): Boolean = true
}
"Then the large method is not inlined" {
programClassPool.classesAccept(
AllMethodVisitor(
AllAttributeVisitor(
methodInliner
)
)
methodInliner,
),
),
)
val lengthAfter = codeAttr.u4codeLength
@@ -187,26 +207,32 @@ class MethodInlinerTest : FreeSpec({
"When using the maximum resulting code length parameter" - {
// Initialize optimization info (used when inlining).
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter()
val optimizationInfoInitializer: ClassVisitor =
MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter(),
),
)
)
programClassPool.classesAccept(optimizationInfoInitializer)
// Create a mock method inliner with the maximum limit
val methodInliner = object : MethodInliner(false, true, MAXIMUM_RESULTING_CODE_LENGTH_JVM, true, true, null) {
override fun shouldInline(clazz: Clazz?, method: Method?, codeAttribute: CodeAttribute?): Boolean = true
}
val methodInliner =
object : MethodInliner(false, true, MAXIMUM_RESULTING_CODE_LENGTH_JVM, true, true, null) {
override fun shouldInline(
clazz: Clazz?,
method: Method?,
codeAttribute: CodeAttribute?,
): Boolean = true
}
programClassPool.classesAccept(
AllMethodVisitor(
AllAttributeVisitor(
methodInliner
)
)
methodInliner,
),
),
)
"Then the large method is inlined" {
@@ -218,10 +244,11 @@ class MethodInlinerTest : FreeSpec({
}
"Given a method initializing a library class and calling a method with backwards branching " - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""class Foo {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""class Foo {
static void f1() {
StringBuilder sb = new StringBuilder();
sb.append(System.currentTimeMillis());
@@ -235,9 +262,9 @@ class MethodInlinerTest : FreeSpec({
System.out.println(i);
}
}
}"""
}""",
),
)
)
val clazz = programClassPool.getClass("Foo") as ProgramClass
val method = clazz.findMethod("f1", "()V") as ProgramMethod
@@ -248,32 +275,38 @@ class MethodInlinerTest : FreeSpec({
"When inlining the method call" - {
// Initialize optimization info (used when inlining).
// Make sure the backwards branching info is set correctly.
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
MultiMemberVisitor(
ProgramMemberOptimizationInfoSetter(),
AllAttributeVisitor(
AllInstructionVisitor(
BackwardBranchMarker()
)
)
)
val optimizationInfoInitializer: ClassVisitor =
MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
MultiMemberVisitor(
ProgramMemberOptimizationInfoSetter(),
AllAttributeVisitor(
AllInstructionVisitor(
BackwardBranchMarker(),
),
),
),
),
)
)
programClassPool.classesAccept(optimizationInfoInitializer)
// Create a mock method inliner which always returns true.
val methodInliner = object : MethodInliner(false, true, true) {
override fun shouldInline(clazz: Clazz?, method: Method?, codeAttribute: CodeAttribute?): Boolean = true
}
val methodInliner =
object : MethodInliner(false, true, true) {
override fun shouldInline(
clazz: Clazz?,
method: Method?,
codeAttribute: CodeAttribute?,
): Boolean = true
}
"Then the method is inlined" {
programClassPool.classesAccept(
AllMethodVisitor(
AllAttributeVisitor(methodInliner)
)
AllAttributeVisitor(methodInliner),
),
)
val lengthAfter = codeAttr.u4codeLength
@@ -284,10 +317,11 @@ class MethodInlinerTest : FreeSpec({
}
"Given a method calling another non-private method in an interface" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""interface Foo {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Foo.java",
"""interface Foo {
default void f1() {
f2();
}
@@ -297,9 +331,9 @@ class MethodInlinerTest : FreeSpec({
sb.append(System.currentTimeMillis());
System.out.println(sb.toString());
}
}"""
}""",
),
)
)
val clazz = programClassPool.getClass("Foo") as ProgramClass
val method = clazz.findMethod("f1", "()V") as ProgramMethod
@@ -308,27 +342,33 @@ class MethodInlinerTest : FreeSpec({
val lengthBefore = codeAttr.u4codeLength
// Initialize optimization info (used when inlining).
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter()
val optimizationInfoInitializer: ClassVisitor =
MultiClassVisitor(
ProgramClassOptimizationInfoSetter(),
AllMethodVisitor(
ProgramMemberOptimizationInfoSetter(),
),
)
)
programClassPool.classesAccept(optimizationInfoInitializer)
// Create a mock method inliner which always returns true.
val methodInliner = object : MethodInliner(false, true, true) {
override fun shouldInline(clazz: Clazz?, method: Method?, codeAttribute: CodeAttribute?): Boolean = true
}
val methodInliner =
object : MethodInliner(false, true, true) {
override fun shouldInline(
clazz: Clazz?,
method: Method?,
codeAttribute: CodeAttribute?,
): Boolean = true
}
"Then the interface method is not inlined" {
programClassPool.classesAccept(
AllMethodVisitor(
AllAttributeVisitor(
methodInliner
)
)
methodInliner,
),
),
)
val lengthAfter = codeAttr.u4codeLength
@@ -342,7 +382,7 @@ private fun printProgramMethodInstructions(
classPool: ClassPool,
className: String,
methodName: String,
methodDescriptor: String
methodDescriptor: String,
): List<String> {
val output = ByteArrayOutputStream()
val pw = PrintWriter(output)
@@ -352,10 +392,13 @@ private fun printProgramMethodInstructions(
methodName,
methodDescriptor,
object : MemberVisitor {
override fun visitProgramMethod(programClass: ProgramClass?, programMethod: ProgramMethod?) {
override fun visitProgramMethod(
programClass: ProgramClass?,
programMethod: ProgramMethod?,
) {
programMethod?.accept(programClass, AllAttributeVisitor(AllInstructionVisitor(ClassPrinter(pw))))
}
}
},
)
}

View File

@@ -14,6 +14,8 @@ import proguard.classfile.Clazz
import proguard.classfile.Member
import proguard.classfile.attribute.annotation.visitor.AllElementValueVisitor
import proguard.classfile.attribute.visitor.AllAttributeVisitor
import proguard.classfile.kotlin.KotlinTypeAliasMetadata
import proguard.classfile.kotlin.visitor.AllTypeAliasVisitor
import proguard.classfile.kotlin.visitor.ReferencedKotlinMetadataVisitor
import proguard.classfile.util.EnumFieldReferenceInitializer
import proguard.classfile.visitor.AllMethodVisitor
@@ -29,45 +31,47 @@ import proguard.util.ProcessingFlagSetter
import proguard.util.ProcessingFlags.DONT_SHRINK
import proguard.util.kotlin.asserter.KotlinMetadataVerifier
private fun beMarkedWith(simpleUsageMarker: SimpleUsageMarker) = object : Matcher<Processable> {
override fun test(value: Processable) =
MatcherResult(
simpleUsageMarker.isUsed(value),
{ "$value should be marked" },
{ "$value should not be used" }
)
}
private fun beMarkedWith(simpleUsageMarker: SimpleUsageMarker) =
object : Matcher<Processable> {
override fun test(value: Processable) =
MatcherResult(
simpleUsageMarker.isUsed(value),
{ "$value should be marked" },
{ "$value should not be used" },
)
}
class ClassUsageMarkerTest : StringSpec({
"Class Usage Marking should mark methods invoked in the method body" {
val (classPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"A.java",
"""
public class A {
public void method1() {
this.method2();
}
public void method2() {
this.method2();
}
public void method3() {
B.method4();
}
}
""".trimIndent()
),
JavaSource(
"B.java",
"""
public class B {
public static void method4() {
new A().method2();
}
}
""".trimIndent()
val (classPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"A.java",
"""
public class A {
public void method1() {
this.method2();
}
public void method2() {
this.method2();
}
public void method3() {
B.method4();
}
}
""".trimIndent(),
),
JavaSource(
"B.java",
"""
public class B {
public static void method4() {
new A().method2();
}
}
""".trimIndent(),
),
)
)
val classA = classPool.getClass("A")
val method1 = classA.findMethod("method1", null)
@@ -104,29 +108,30 @@ class ClassUsageMarkerTest : StringSpec({
}
"The comparable interface should induce additional marking" {
val (classPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"Application.java",
"""
public class Application {
Other attribute;
public Application() {
attribute = new Other();
}
}
""".trimIndent()
),
JavaSource(
"Other.java",
"""
public class Other implements Comparable<Other> {
public void foo() {}
public void bar() {}
public int compareTo(Other o) { foo(); return 0; }
}
""".trimIndent()
val (classPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"Application.java",
"""
public class Application {
Other attribute;
public Application() {
attribute = new Other();
}
}
""".trimIndent(),
),
JavaSource(
"Other.java",
"""
public class Other implements Comparable<Other> {
public void foo() {}
public void bar() {}
public int compareTo(Other o) { foo(); return 0; }
}
""".trimIndent(),
),
)
)
val classUsageMarker = ClassUsageMarker()
val applicationClazz = classPool.getClass("Application")
@@ -146,24 +151,25 @@ class ClassUsageMarkerTest : StringSpec({
}
"Using an enum as default value in an annotation field should mark the enum value as used" {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
JavaSource(
"A.java",
"""
public @interface A {
B b() default B.Y;
}
""".trimIndent()
),
JavaSource(
"B.java",
"""
public enum B {
X, Y, Z
}
""".trimIndent()
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
JavaSource(
"A.java",
"""
public @interface A {
B b() default B.Y;
}
""".trimIndent(),
),
JavaSource(
"B.java",
"""
public enum B {
X, Y, Z
}
""".trimIndent(),
),
)
)
// Make sure the field references in enum fields are updated.
programClassPool.classesAccept(
@@ -171,17 +177,17 @@ class ClassUsageMarkerTest : StringSpec({
true,
AllElementValueVisitor(
true,
EnumFieldReferenceInitializer()
)
)
EnumFieldReferenceInitializer(),
),
),
)
// Visit attributes to mark usage processing info.
programClassPool.getClass("A").accept(
AllAttributeVisitor(
true,
ClassUsageMarker()
)
ClassUsageMarker(),
),
)
val bClass = programClassPool.getClass("B")
@@ -195,19 +201,20 @@ class ClassUsageMarkerTest : StringSpec({
}
"Given a Kotlin interface with default method implementation in compatibility mode" {
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
interface Test {
fun foo() {
TODO()
}
}
""".trimIndent()
),
kotlincArguments = listOf("-Xjvm-default=all")
)
val (programClassPool, libraryClassPool) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Test.kt",
"""
interface Test {
fun foo() {
TODO()
}
}
""".trimIndent(),
),
kotlincArguments = listOf("-Xjvm-default=all"),
)
// Run the asserter to ensure any metadata that isn't initialized correctly is thrown away
KotlinMetadataVerifier(Configuration()).execute(AppView(programClassPool, libraryClassPool))
@@ -219,23 +226,27 @@ class ClassUsageMarkerTest : StringSpec({
}
}
"Given a Kotlin interface with default method implementation" {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
KotlinSource(
"Interface.kt",
"""
package test;
interface Interface {
fun foo() : Int {
return 42;
}
}
""".trimIndent(),
),
)
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
KotlinSource(
"Interface.kt",
"""
package test;
interface Interface {
fun foo() : Int {
return 42;
}
}
""".trimIndent(),
),
)
// Necessary to force marking methods that are not actually used and have not been processed by the Marker.
class CustomMarker(var marker: SimpleUsageMarker) : MemberVisitor {
override fun visitAnyMember(clazz: Clazz, member: Member) {
override fun visitAnyMember(
clazz: Clazz,
member: Member,
) {
marker.markAsUsed(member)
}
}
@@ -247,7 +258,9 @@ class ClassUsageMarkerTest : StringSpec({
programClassPool.classesAccept(classUsageMarker)
// Mark the default implementation as used.
programClassPool.accept(NamedClassVisitor(NamedMethodVisitor("foo", null, CustomMarker(usageMarker)), "test/Interface\$DefaultImpls"))
programClassPool.accept(
NamedClassVisitor(NamedMethodVisitor("foo", null, CustomMarker(usageMarker)), "test/Interface\$DefaultImpls"),
)
// Process Kotlin metadata: this should cause the interface method to be kept as well.
programClassPool.classesAccept(ReferencedKotlinMetadataVisitor(classUsageMarker))
@@ -255,4 +268,39 @@ class ClassUsageMarkerTest : StringSpec({
val fooInterface = programClassPool.getClass("test/Interface").findMethod("foo", null)
fooInterface should beMarkedWith(usageMarker)
}
"Given Kotlin `typealias` declarations where one aliases another" {
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
KotlinSource(
"KotlinTypeAlias.kt",
"""
typealias P = () -> Unit
typealias A = P
""".trimIndent(),
),
kotlincArguments = listOf("-language-version=1.9"),
)
// Obtain the `typealias` declarations A and P
val typeAliasList = mutableListOf<KotlinTypeAliasMetadata>()
programClassPool.classesAccept(
ReferencedKotlinMetadataVisitor(
AllTypeAliasVisitor { _, _, kotlinTypeAliasMetadata ->
typeAliasList.add(kotlinTypeAliasMetadata)
},
),
)
// Ensure only `typealias` A and `typealias` P are present
typeAliasList.map { it.name }.toSet() shouldBe setOf("A", "P")
// Both `typealias` A and `typealias` P should be marked as used by usageMarker.
val usageMarker = SimpleUsageMarker()
val classUsageMarker = ClassUsageMarker(usageMarker)
programClassPool.classesAccept(classUsageMarker)
typeAliasList.forEach {
it should beMarkedWith(usageMarker)
}
}
})

View File

@@ -17,10 +17,11 @@ import proguard.testutils.ClassPoolBuilder
class ProguardAssemblerTest : FreeSpec({
"Given Java bytecode" - {
val (programClassPool, _) = ClassPoolBuilder.fromSource(
AssemblerSource(
"A.jbc",
"""
val (programClassPool, _) =
ClassPoolBuilder.fromSource(
AssemblerSource(
"A.jbc",
"""
version 1.8;
public class A extends java.lang.Object [
SourceFile "A.java";
@@ -43,9 +44,9 @@ class ProguardAssemblerTest : FreeSpec({
}
}
"""
""",
),
)
)
"When the ClassPool object is created" - {
programClassPool.shouldNotBeNull()
"Then the count and name of the methods should match the bytecode" {

View File

@@ -30,9 +30,11 @@ val currentJavaVersion: Int by lazy {
return@lazy version.toInt()
}
fun isJava9OrLater(): Boolean =
SourceVersion.latestSupported() > SourceVersion.RELEASE_8
fun isJava9OrLater(): Boolean = SourceVersion.latestSupported() > SourceVersion.RELEASE_8
fun getCurrentJavaHome(): File =
if (isJava9OrLater()) File(System.getProperty("java.home"))
else File(System.getProperty("java.home")).parentFile
if (isJava9OrLater()) {
File(System.getProperty("java.home"))
} else {
File(System.getProperty("java.home")).parentFile
}

View File

@@ -16,23 +16,26 @@ import proguard.util.ProcessingFlags.DONT_OPTIMIZE
import proguard.util.ProcessingFlags.DONT_PROCESS_KOTLIN_MODULE
import proguard.util.ProcessingFlags.DONT_SHRINK
fun hasFlag(flag: Int) = object : Matcher<Int> {
override fun test(value: Int): MatcherResult =
MatcherResult(
(value and flag) != 0,
{ "Flag ${flag.asProcessingFlagString} should be set" },
{ "Flag ${flag.asProcessingFlagString} should not be set" }
)
}
fun hasFlag(flag: Int) =
object : Matcher<Int> {
override fun test(value: Int): MatcherResult =
MatcherResult(
(value and flag) != 0,
{ "Flag ${flag.asProcessingFlagString} should be set" },
{ "Flag ${flag.asProcessingFlagString} should not be set" },
)
}
infix fun Int.shouldHaveFlag(flag: Int) = this should hasFlag(flag)
infix fun Int.shouldNotHaveFlag(flag: Int) = this shouldNot hasFlag(flag)
val Int.asProcessingFlagString: String
get() = when (this) {
DONT_OBFUSCATE -> "DONT_OBFUSCATE"
DONT_SHRINK -> "DONT_SHRINK"
DONT_OPTIMIZE -> "DONT_OPTIMIZE"
DONT_PROCESS_KOTLIN_MODULE -> "DONT_PROCESS_KOTLIN_MODULE"
else -> this.toString()
}
get() =
when (this) {
DONT_OBFUSCATE -> "DONT_OBFUSCATE"
DONT_SHRINK -> "DONT_SHRINK"
DONT_OPTIMIZE -> "DONT_OPTIMIZE"
DONT_PROCESS_KOTLIN_MODULE -> "DONT_PROCESS_KOTLIN_MODULE"
else -> this.toString()
}

View File

@@ -24,10 +24,15 @@ object TestConfig : AbstractProjectConfig() {
}
class RequiresJavaVersionAnnotationFilter : SpecFilter {
override fun filter(kclass: KClass<*>): SpecFilterResult = if (with(kclass.findAnnotation<RequiresJavaVersion>()) {
(this == null || (currentJavaVersion >= this.from && currentJavaVersion <= this.to))
}
) Include else Exclude("Required Java version is not in range.")
override fun filter(kclass: KClass<*>): SpecFilterResult =
if (with(kclass.findAnnotation<RequiresJavaVersion>()) {
(this == null || (currentJavaVersion >= this.from && currentJavaVersion <= this.to))
}
) {
Include
} else {
Exclude("Required Java version is not in range.")
}
}
@Target(AnnotationTarget.CLASS)

View File

@@ -2,6 +2,7 @@ plugins {
id 'distribution'
id 'io.github.gradle-nexus.publish-plugin'
id 'signing'
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
}
allprojects {
@@ -23,6 +24,7 @@ task buildDocumentation(type: Exec) {
nexusPublishing {
repositories {
sonatype {
nexusUrl = uri("https://ossrh-staging-api.central.sonatype.com/service/local/")
username = findProperty('PROGUARD_STAGING_USERNAME')
password = findProperty('PROGUARD_STAGING_PASSWORD')
}

View File

@@ -16,11 +16,8 @@ ProGuard is currently hosted on GitHub:
[source code](https://github.com/Guardsquare/proguard) yourself and create
[pull requests](https://github.com/Guardsquare/proguard/pulls).
!!! tip
The [***Guardsquare Community***](https://community.guardsquare.com/) is the place to be for all your ProGuard-related questions and feedback.
You may also find answers on
[Stack Overflow](http://stackoverflow.com/questions/tagged/proguard).
- You may also find answers on
[Stack Overflow](http://stackoverflow.com/questions/tagged/proguard).
ProGuard used to be hosted on Sourceforge:

View File

@@ -1,4 +1,4 @@
Welcome to the manual for **ProGuard** version 7.5 ([what's new?](releasenotes.md)).
Welcome to the manual for **ProGuard** version 7.8.0 ([what's new?](releasenotes.md)).
ProGuard is an open-sourced Java class file shrinker, optimizer, obfuscator, and
preverifier. As a result, ProGuard processed applications and libraries are smaller and faster.

View File

@@ -1,3 +1,35 @@
## Version 7.8
### Kotlin support
- Add support for Kotlin 2.2.
### Java support
- Add support for Java 25. (#481)
## Version 7.7
### Java support
- Add support for Java 24. (#458)
### Bugfixes
- Prevent `IllegalArgumentException` when strings longer than 65535 bytes are present in the application (#267).
- Prevent `StackOverflowException` when processing a pattern match switch (#444).
### Improved
- Improve processing time in apps where a large number of linked methods are present.
## Version 7.6.1
### Bugfixes
- Fix backporting default interface method parameter annotations. (#451)
- Prevent `Value in slot <n> is empty` exception during processing time by no longer applying lower slot replacement by default. It can be enabled again with the `optimization.enable.slot.replacement` system property.
## Version 7.6
### Java support

View File

@@ -1,8 +0,0 @@
# AppSweep
[AppSweep](https://appsweep.guardsquare.com/) is a free online app security testing tool that allows you to find and fix security
issues in your Android app's code and dependencies.
<center>
![](appsweep.png)
</center>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -9,7 +9,7 @@ have been replaced by short meaningless strings. Source file names and
line numbers are missing altogether. While this may be intentional, it
can also be inconvenient when debugging problems.
<img src="PG_ReTrace.png" alt="ReTrace deobfuscation workflow" style="display: block; margin-left: auto; margin-right: auto;" />
<img src="./PG_ReTrace.png" alt="ReTrace deobfuscation workflow" style="display: block; margin-left: auto; margin-right: auto;" />
ReTrace can read an obfuscated stack trace and restore it to what it
would look like without obfuscation. The restoration is based on the
@@ -18,9 +18,10 @@ file links the original class names and class member names to their
obfuscated names.
## Usage {: #usage }
[The Retrace jar is published individually in Maven](https://mvnrepository.com/artifact/com.guardsquare/proguard-retrace/latest), you can also find it in the `lib` directory of the [ProGuard
distribution](https://github.com/Guardsquare/proguard/releases/latest).
You can find the ReTrace jar in the `lib` directory of the ProGuard
distribution. To run ReTrace, just type:
To run ReTrace, just type:
`java -jar retrace.jar `\[*options...*\] *mapping\_file*
\[*stacktrace\_file*\]
@@ -50,7 +51,7 @@ The following options are supported:
de-obfuscate more general types of input than just stack traces. A relatively
simple expression like this works for basic stack trace formats:
(?:.*? at %c\.%m\(%s(?::%l)?\))|(?:(?:.*?[:"] +)?%c(?::.*)?)
(?:.*? at %c\.%m\(%s(?::%l)?\))|(?:(?:.*?[:\"] +)?%c(?::.*)?)
It for instance matches the following lines:

View File

@@ -2,9 +2,6 @@ While preparing a configuration for processing your code, you may bump
into a few problems. The following sections discuss some common issues
and solutions:
!!! tip
Whenever you don't find the solution for your issue on this page, the [***Guardsquare Community***](https://community.guardsquare.com/) might be the place to check for an answer or post a question.
## Problems while processing {: #processing}

View File

@@ -91,7 +91,6 @@ nav:
- Tools:
- ReTrace: manual/tools/retrace.md
- Playground: manual/tools/playground.md
- AppSweep: manual/tools/appsweep.md
- Troubleshooting:
- Overview: manual/troubleshooting/troubleshooting.md
- Limitations: manual/troubleshooting/limitations.md

View File

@@ -7,7 +7,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.6.0'
classpath 'com.guardsquare:proguard-gradle:7.8.0'
}
}
@@ -31,16 +31,12 @@ test {
useJUnitPlatform()
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
kotlin {
jvmToolchain(8)
}
application {
mainClassName = 'AppKt'
mainClass.set('com.example.AppKt')
}
ext.baseCoordinates = "${project.name}-${project.version}"

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -7,7 +7,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.6.0'
classpath 'com.guardsquare:proguard-gradle:7.8.0'
}
}

View File

Binary file not shown.

View File

@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +82,12 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -133,22 +134,29 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
@@ -205,6 +217,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

View File

@@ -13,8 +13,10 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +27,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@@ -5,7 +5,7 @@ buildscript {
google()
}
dependencies {
classpath("com.guardsquare:proguard-gradle:7.6.0")
classpath("com.guardsquare:proguard-gradle:7.8.0")
}
}
@@ -23,13 +23,13 @@ dependencies {
}
application {
mainClassName = "gradlekotlindsl.App"
mainClass = "gradlekotlindsl.App"
}
tasks.withType<Jar> {
manifest {
attributes["Main-Class"] = application.mainClassName
attributes["Main-Class"] = application.mainClass
}
}

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

Some files were not shown because too many files have changed in this diff Show More