mirror of
https://github.com/Guardsquare/proguard.git
synced 2026-03-13 09:50:34 +08:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
869ce156b1 | ||
|
|
124b33e473 | ||
|
|
b8a62c8ca8 | ||
|
|
886477806c | ||
|
|
7fc907d1fb | ||
|
|
b10346ba32 | ||
|
|
f2ced20be4 | ||
|
|
35ea6d587f | ||
|
|
eea0ccbe8f | ||
|
|
bee74a9963 | ||
|
|
4b4aa93335 | ||
|
|
4781f5898f | ||
|
|
1f9a4a1b94 | ||
|
|
40f9222bc3 | ||
|
|
ef6a8352bd | ||
|
|
e225e56a8d | ||
|
|
4288cce536 | ||
|
|
3456cf330e | ||
|
|
fbcf41fd67 | ||
|
|
bacde1cede | ||
|
|
430a04502d | ||
|
|
08adfa5552 | ||
|
|
89b1e55ea2 | ||
|
|
f4c4a13a90 | ||
|
|
c1eafc7b6b | ||
|
|
73860de626 | ||
|
|
ff66baaced | ||
|
|
174d3f4155 | ||
|
|
f5352fece7 | ||
|
|
844f3d76be | ||
|
|
7b6712e840 |
22
.github/workflows/continuous_integration.yml
vendored
22
.github/workflows/continuous_integration.yml
vendored
@@ -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
1
.gitignore
vendored
@@ -7,3 +7,4 @@ local.properties
|
||||
/lib/
|
||||
docs/html
|
||||
.kotlin
|
||||
.DS_Store
|
||||
|
||||
@@ -81,8 +81,7 @@ number of facade classes that construct and run these chains. Notably:
|
||||
- `Initializer` initializes links between the code and resources, to traverse
|
||||
the data structures more easily.
|
||||
|
||||
- `Marker` marks code and resources to be kept, encrypted, etc., based on the
|
||||
configuration.
|
||||
- `Marker` marks code and resources to be kept etc. based on the configuration.
|
||||
|
||||
- `Backporter` backports code to older versions of Java.
|
||||
|
||||
@@ -103,8 +102,8 @@ At a high level, the flow of data inside ProGuard is as follows:
|
||||
|
||||
- Traverse the input data, to parse any useful data structures.
|
||||
|
||||
- Process the data structures in a number of steps (mainly shrinking, string
|
||||
encryption, optimization, obfuscation, class encryption).
|
||||
- Process the data structures in a number of steps (mainly shrinking, optimization,
|
||||
obfuscation).
|
||||
|
||||
- Traverse the input data again, this time to write to the output, by copying,
|
||||
transforming, replacing, or removing data entries. The transformations can
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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)));
|
||||
|
||||
21
base/src/main/java/proguard/normalize/StringNormalizer.java
Normal file
21
base/src/main/java/proguard/normalize/StringNormalizer.java
Normal 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)));
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)))));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ implements AttributeVisitor,
|
||||
*/
|
||||
public EvaluationShrinker()
|
||||
{
|
||||
this(new PartialEvaluator(), true, false, null, null);
|
||||
this(PartialEvaluator.Builder.create().build(), true, false, null, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@ implements ClassVisitor,
|
||||
*/
|
||||
public SimpleEnumUseChecker()
|
||||
{
|
||||
this(new PartialEvaluator(new TypedReferenceValueFactory()));
|
||||
this(PartialEvaluator.Builder.create().setValueFactory(new TypedReferenceValueFactory()).build());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ implements AttributeVisitor,
|
||||
*/
|
||||
public SimpleEnumUseSimplifier()
|
||||
{
|
||||
this(new PartialEvaluator(new TypedReferenceValueFactory()), null);
|
||||
this(PartialEvaluator.Builder.create().setValueFactory(new TypedReferenceValueFactory()).build(), null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -192,6 +192,7 @@ implements KotlinMetadataVisitor,
|
||||
kotlinPropertyMetadata.setterSignature = null;
|
||||
kotlinPropertyMetadata.referencedSetterMethod = null;
|
||||
kotlinPropertyMetadata.flags.isVar = false;
|
||||
kotlinPropertyMetadata.setterParameter = null;
|
||||
kotlinPropertyMetadata.setterParameters.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 **"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
47
base/src/test/kotlin/proguard/obfuscate/AnnotationTest.kt
Normal file
47
base/src/test/kotlin/proguard/obfuscate/AnnotationTest.kt
Normal 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",
|
||||
),
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -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" {
|
||||
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;"
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;")
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||

|
||||
</center>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 68 KiB |
@@ -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:
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,7 +7,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.guardsquare:proguard-gradle:7.6.0'
|
||||
classpath 'com.guardsquare:proguard-gradle:7.8.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
|
||||
44
examples/application/gradlew
vendored
44
examples/application/gradlew
vendored
@@ -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.
|
||||
|
||||
37
examples/application/gradlew.bat
vendored
37
examples/application/gradlew.bat
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user