Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of lambda merging for Kotlin lambda classes #257

Open
wants to merge 223 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 200 commits
Commits
Show all changes
223 commits
Select commit Hold shift + click to select a range
7234690
Skeleton for new KotlinLambdaMerger to be used between shrinking and …
Dec 28, 2021
b45bb36
KotlinLambdaMerger: find lambda & function0 classes + find subclasses
Dec 28, 2021
e1e33ef
Return Function0 interface class
Dec 28, 2021
1f94231
Filter on lambda classes with 0-argument constructor
Dec 28, 2021
4f7c618
PackageGrouper: a ClassVisitor that creates a class pool per package
Jan 1, 2022
26a90ae
MethodCopier: a MemberVisitor that copies methods to a destination class
Jan 1, 2022
0a68701
LambdaGroupInvokeCodeBuilder: compose code for lambda group invoke
Jan 1, 2022
a92c963
KotlinLambdaEnclosingMethodUpdater: lambda group instead of lambda group
Jan 1, 2022
60d6326
KotlinLambdaClassMerger: create a lambda group for the given classpool
Jan 1, 2022
3596717
KotlinLambdaMerger: constants for each FunctionX interface
Jan 1, 2022
53bd917
KotlinLambdaMerger: imports
Jan 1, 2022
190d8f2
KotlinLambdaMerger: extraDataEntryNameMap as constructor argument
Jan 1, 2022
e9f2775
KotlinLambdaMerger: use new constants
Jan 1, 2022
b53739b
KotlinLambdaClassMerger: use new constants
Jan 1, 2022
a43d0de
ProGuard: pass extraDataEntryNameMap to lambda merger
Jan 1, 2022
438709e
KotlinLambdaMerger implemented
Jan 1, 2022
42cdc99
ProGuard: call shrinker after lambda merging
Jan 1, 2022
5fe4112
Test for PackageGrouper & checking whether lambda merger reduces numb…
Jan 1, 2022
16a95e7
KotlinLambdaClassMerger: remove unused invoke method variable
Jan 1, 2022
3f54119
KLCM: make invokeMethod local variable
Jan 1, 2022
c0e70ba
KLCM: make constructor arguments final
Jan 1, 2022
3e7cf66
Comment log statements
Jan 2, 2022
0d24de5
Messy MethodInlinerWrapper class
Jan 2, 2022
cdddc97
Use MethodInliner for inlining invoke methods before & after merging
Jan 3, 2022
e8b70e5
Copy lambda bridge invoke method to lambda group (after inlining spec…
Jan 3, 2022
fdb8426
Comment on invoke inlining assumption
Jan 3, 2022
43157e8
Cast invoke Method to ProgramMethod
Jan 3, 2022
0cd65e8
KotlinLambdaMergerTest: assert all referenced classes are in class pool
Jan 3, 2022
a3a662a
Correct comment in test
Jan 3, 2022
506ea6f
Add proguard-core with relative build path to settings.gradle
Jan 4, 2022
94ee87f
Update CI action to refer to proguard-core fork
Jan 4, 2022
604fae1
Set path for proguard-core fork
Jan 4, 2022
4b96ecc
Update path for proguard-core fork
Jan 4, 2022
d95e082
Remove unused import of wrong package
Jan 4, 2022
340fe16
Move PackageGrouper to ProGuard-CORE
Jan 5, 2022
b6e9ea1
Move MethodCopier to ProGuard-CORE
Jan 5, 2022
878aa06
Remove PackageGrouper tests from KotlinLambdaMergerTest
Jan 5, 2022
21ad167
Make MethodCopier public to be used in KotlinLambdaClassMerger
Jan 5, 2022
0e41f06
Separate builder for entire lambda group invoke method
Jan 6, 2022
d87fc35
Separate builder for entire lambda group <init> constructor
Jan 6, 2022
0e67524
Separate builder for lambda groups
Jan 6, 2022
2c4c0e3
Add lambdaGroup property to ProgramClassOptimizationInfo
Jan 6, 2022
82b4d0c
Method to check whether lambda should be merged
Jan 6, 2022
764279a
Add optimisation info to lambda's before merging
Jan 6, 2022
c21c7a9
Don't inline before/after entire lambda merging process
Jan 6, 2022
ef3c2dd
Remove unused imports
Jan 6, 2022
17afac1
KotlinLambdaClassMerger as ClassPoolVisitor uses lambda group builder
Jan 6, 2022
6efa42b
Pass configuration to constructor of lambda class merger
Jan 6, 2022
29e30dc
Remove old (unrelevant) comments
Jan 6, 2022
eb41035
Clean classes before lambda merging
Jan 6, 2022
4d22e85
Invoke lambda group init without reference (to be initialised later)
Jan 6, 2022
9199563
Initialise all classes after lambda merging
Jan 6, 2022
f99d34d
Add lambd class interfaces to lambda group
Jan 6, 2022
3ba9c43
Referenced class of RefConstant can be null
Jan 6, 2022
a4741bd
Add support for merging FunctionN lambda's
Jan 6, 2022
04c434b
Fix: load lambda group before invoke method arguments
Jan 6, 2022
ea38c1a
Use separate invoke method builder for FunctionN lambda's
Jan 6, 2022
012eeea
Merge lambda's of all arities (with empty closure)
Jan 6, 2022
b3178bd
Only inline methods of same class for lambda merging
Jan 7, 2022
1aeee85
Class must extend to be merged
Jan 7, 2022
af673b6
Filter DONT_OPTIMIZE classes before merging + inline lambda groups
Jan 7, 2022
345e34e
Set optimisation info lambda group of lambda group
Jan 7, 2022
f024bbf
Add lambda group as dependency of enclosing classes
Jan 7, 2022
99ae022
Don't inline lambda group methods right after creation
Jan 7, 2022
5cde21e
Don't visit inner lambda's that have wrong constructor
Jan 7, 2022
44032ff
Set optimisation info of lambda before visiting inner classes
Jan 7, 2022
781f946
Use static method inliner for lambda groups
Jan 7, 2022
90852c1
Method inlining within class
Jan 7, 2022
d23de15
Remove unused imports
Jan 7, 2022
031193e
Set INJECTED flag on newly built lambda groups
Jan 8, 2022
6e519f0
Use SameClassMethodInliner to inline invoke methods within lambda class
Jan 8, 2022
d9b384a
After merging: shrink Lambda Groups instead of all classes
Jan 8, 2022
35736d4
Use SameClassMethodInliner to inline invoke helper methods within LG's
Jan 8, 2022
4fd0094
Don't require Function0 to start merging
Jan 8, 2022
ed1e962
Set OptimizationInfo, if necessary, before lambda merging
Jan 8, 2022
76b6eb9
Code outlining
Jan 8, 2022
ef1629b
Apply the LineNumberLinearizer to all lambda groups after inlining
Jan 8, 2022
5ff81d5
Make lambda group name public field
Jan 8, 2022
e1acdf2
Mark all non-LG classes and methods as used before shrinking LG's
Jan 8, 2022
db8a8cd
Keep interfaces of lambda groups
Jan 8, 2022
9a5703e
Don't try to merge classes that don't have optimisation info
Jan 8, 2022
f3ad14f
Use code patterns to replace instructions in enclosing method
Jan 8, 2022
e147b4b
Keep track of lambda's that are not merged + don't merge single lambda's
Jan 8, 2022
1de8e87
Make info statements debug
Jan 8, 2022
6975785
Remove redundant comment
Jan 8, 2022
3f3e552
Remove unused imports
Jan 9, 2022
02e996e
Set access flags of LG to FINAL and SUPER
Jan 9, 2022
aa30700
Replace info logging by trace logging
Jan 9, 2022
2010400
Arity as constructor argument of lambda group
Jan 9, 2022
9d74ea0
Use ModifiedAllInnerClassesInfoVisitor to visit all inner classes
Jan 9, 2022
3d0f394
Remove unused LambdaGroupInvokeCodeBuilder class
Feb 8, 2022
7ba723f
Revert "Only inline methods of same class for lambda merging" + remov…
Feb 8, 2022
ee4c6a9
Passify KotlinLambdaMerger
Feb 17, 2022
a7d453a
Add new option to configuration: -mergekotlinlambdaclasses
Feb 24, 2022
3cbec14
Add new option to configuration: -printlambdagroupmapping
Feb 24, 2022
514ecbf
Print mapping of lambda's to lambda group
Feb 24, 2022
bf3fb8a
Merge branch 'master' into feature-lambda-merging
Feb 24, 2022
304a6e2
Enable lambda merging based on configuration
Mar 22, 2022
8e95bfd
Mention lambda class id in mapping
Mar 22, 2022
0b74332
Call different <init> of lambda group, depending on closure size
Mar 22, 2022
e91d24a
KotlinLambdaGroupInitUpdater
Mar 22, 2022
7e0318e
Use the init updater for building a lambda group
Mar 22, 2022
f3d5717
Use descriptor to select init when instantiating lambda group
Mar 22, 2022
fb316d9
Copy lambda init methods into lambda group + updated method copier
Mar 22, 2022
f834eca
Increase maximum code size of invoke methods
Mar 22, 2022
bb64f6c
Consider lambda's with constructor arguments for merging
Mar 22, 2022
5f6bb43
Add space after comma
Mar 24, 2022
6a99a3d
Fix bug related to miscounting Double/Long args as 1 byte instead of 2
Mar 24, 2022
7150365
Don't merge lambda's that don't have exactly 1 constructor
Mar 24, 2022
809f19c
Exclude certain lambda classes from merging
Mar 27, 2022
c4832fd
Set optimisation info on lambda groups, when created
Mar 27, 2022
8f2cf0f
Set lambda group class as the target class of merged lambda's
Mar 27, 2022
52217a5
Support merging lambda classes that have conflicting constructors
Mar 27, 2022
9869a3e
Add optimisation info to all program classes before merging
Mar 27, 2022
63168e9
Method for looking up (non)-bridge invoke method of lambda
Mar 27, 2022
0193e4f
KotlinLambdaEnclosingMethodUpdater extended
Mar 27, 2022
3160366
Merge branch 'fix-conflicting-duplicate-init' into feature-lambda-mer…
Mar 27, 2022
66bc9b6
New configuration option: -lambdamergingafteroptimizing
May 7, 2022
fcb6606
Updated references to lambda class in all classes of same package
May 7, 2022
971428b
Find inner lambda's by looking for class constants of lambda classes
May 7, 2022
5f50d83
Formal rules for lambda's to be merged
May 7, 2022
75288d6
Mark lambda groups as used before shrinking them
May 7, 2022
698cc26
New configuration option: -mergelambdaclasseswithunexpectedmethods
May 7, 2022
c7c8745
Methods for checking lambda merging preconditions
May 7, 2022
8295039
Optimised imports
May 7, 2022
029c037
Pretty log printing of lambda class/lambda group names
May 7, 2022
53bae1d
Enclosing class cannot be lambda class itself
May 7, 2022
af15b14
Fix imports
May 7, 2022
fbb14fd
Merge branch 'feature-lambda-merging-closures-ctors' into feature-lam…
May 7, 2022
02ad996
Merge branch 'feature-lambda-merging'
May 7, 2022
01af867
Merge remote-tracking branch 'Guardsquare/master'
May 7, 2022
4fce5c1
Remove comment
May 12, 2022
a8d205c
Remove unused function name constants
May 12, 2022
19525a9
2 new/modified lambda merging requirements
May 12, 2022
61dc16c
Apply LineAttributeTrimmer to lambda groups
May 12, 2022
012d183
Clean all classes after lambda merging
May 12, 2022
eaa2f35
Remove unused arguments from helper methods
May 12, 2022
6de2bb8
Mark invoke, <init> and <clinit> methods as used before shrinking lam…
May 12, 2022
545b67f
Remove commented code
May 12, 2022
9a03344
Allow inlining of big methos in lambda classes
May 12, 2022
3b7503b
Remove lambda class as subclass of its interfaces and super class
May 12, 2022
641e106
Use constant from KotlinConstants
May 12, 2022
0816334
Update any constants where a merged lambda class is used as type or name
May 12, 2022
4c1a86d
Move responsibility of extending <init> descriptor to init updater
Jun 4, 2022
be44e40
LambdGroupMappingPrinter: also print arity based on interface
Jun 4, 2022
059d918
Merge branch 'refactoring' into feature-lambda-merging
Jun 4, 2022
f6e191b
KotlinLambdaGroupInitUpdaterTest
Jun 4, 2022
9b60b16
KotlinLambdaMergerTest updated to use new KotlinLambdaMerger
Jun 4, 2022
77f8204
Merge branch 'feature-lambda-merging'
Jun 4, 2022
e4c9162
Remove commented code
Jun 4, 2022
ed6fc74
Remove unused canMerge method
Jun 4, 2022
13032ae
Merge branch 'refactoring' into feature-lambda-merging
Jun 4, 2022
7531f29
Don't allow merging when class is not a lambda class
Jun 4, 2022
1f70c32
Execute lambda merger before last iteration of optimiser, if not earlier
Jun 4, 2022
7489992
Merge branch 'feature-lambda-merging-in-optimizer' into feature-lambd…
Jun 4, 2022
decb49a
Merge branch 'feature-lambda-merging'
Jun 4, 2022
0fbed66
Test whether lambda group has been added to package
Jun 4, 2022
ac8e902
Remove dash ('-') from unit tests
Jun 4, 2022
089fca6
Merge branch 'feature-lambda-merging'
Jun 4, 2022
5ba88c1
MatchDetector in separate file
Jun 6, 2022
d07edaf
New class: InstructionSequenceCollector
Jun 6, 2022
d3c257b
Test added for KotlinLambdaGroupInvokeMethodBuilder
Jun 6, 2022
a753366
Merge branch 'feature-lambda-merging'
Jun 6, 2022
8045e78
Merge branch 'Guardsquare:master' into master
heckej Jun 6, 2022
b19f341
Apply ktlint to test classes
Jun 6, 2022
49debdf
Test added for KotlinLambdaEnclosingMethodUpdater
Jun 6, 2022
a85084f
Merge branch 'feature-lambda-merging'
Jun 6, 2022
b2d8b57
Skeleton for KotlinLambdaGroupBuilderTest
Jun 7, 2022
784e884
Tests for the KotlinLambdaGroupBuilder
Jun 7, 2022
8d5c4d7
Merge branch 'feature-lambda-merging'
Jun 7, 2022
ee581ef
Unused class KotlinLambdaGroupBuilder removed
Jun 7, 2022
5af6581
Merge branch 'refactoring' into feature-lambda-merging
Jun 7, 2022
8b78b7c
Merge branch 'refactoring'
Jun 7, 2022
dc7cd78
Code alignment
Jun 8, 2022
fb0276d
Unused code blocks removed
Jun 8, 2022
5888379
Inner classes moved to ProGuard CORE
Jun 8, 2022
43de408
Move method within KotlinLambdaEnclosingMethodUpdater
Jun 8, 2022
1eaeaca
Refer to existing "invoke" constant
Jun 8, 2022
bf63c23
Documentation comments
Jun 8, 2022
fb3d726
Formal merging check methods rewritten
Jun 8, 2022
a216de3
Imports + reference to existing "invoke" constant
Jun 8, 2022
20e0ede
Code alignment
Jun 8, 2022
4aae5ca
Move anonymous class to separate file in ProGuard CORE
Jun 8, 2022
c985d70
Merge branch 'code-refactoring' into feature-lambda-merging
Jun 8, 2022
7f46180
Use FieldRenamer without anonymous class
Jun 8, 2022
99b18e5
Correct usage of visitor for (un)setting access flags
Jun 8, 2022
705617a
Code alignment
Jun 8, 2022
77963f9
Merge branch 'code-refactoring' into feature-lambda-merging
Jun 8, 2022
8f9fc01
Not-working Kotlin Lambda Merger LambdaGroupBuilder tests disabled
Jun 8, 2022
6019bc8
Merge branch 'feature-lambda-merging'
Jun 8, 2022
d7c510b
Correct refactoring bug: check static methods
Jun 8, 2022
970f5a0
Merge branch 'feature-lambda-merging'
Jun 8, 2022
ae4eb58
Fix refactoring bug: don't consider fields of same class as referenced
Jun 8, 2022
c61c41c
Preverify lambda groups, such that the correct attributes are added
Jun 8, 2022
4a58dd4
Output verification test works
Jun 8, 2022
b865268
Merge branch 'feature-lambda-merging'
Jun 8, 2022
732a889
Remove not working tests
Jun 8, 2022
fb8b417
Merge branch 'feature-lambda-merging'
Jun 8, 2022
1b997bb
Output verification test for lambda group builder
Jun 8, 2022
091794f
Merge branch 'feature-lambda-merging'
Jun 8, 2022
7c28e60
Move visitors from PGC to ProGuard
Jun 29, 2022
b4ac2fb
Update references to moved Kotlin constants
Jun 29, 2022
95fc370
Merge branch 'refactoring-for-pull-request'
Jun 29, 2022
baab6b9
Move ModifiedAllInnerClassesInfoVisitor from PGC to ProGuard
Jun 29, 2022
0eb6c83
Move FieldCopier from PGC to ProGuard
Jun 29, 2022
79f7656
MethodCopierTest moved from PGC to ProGuard
Jun 29, 2022
ba4d80f
Replace `info` logging statement with `trace`
heckej Jun 29, 2022
4426bb6
Use lambda merging in configuration as optimisation option
Jun 29, 2022
93112b2
Merge remote-tracking branch 'origin/master'
Jun 29, 2022
3cf0245
Code alignment
Jun 29, 2022
b5f6c25
'{' on new line
Jun 29, 2022
d99f1a1
Merge branch 'refactoring-for-pull-request'
Jun 29, 2022
ea7bcb2
Remove 'unexpected methods' check
Jun 29, 2022
c28a14f
Use MultiClassVisitor to group class visitors
Jun 29, 2022
6944e36
Add mergedLambdaVisitor + use it to count merged lambda classes
Jun 29, 2022
492d73b
Remove ClassCastException handling of visitCodeAttribute in init updater
Jun 29, 2022
210a0a5
Replace `warn'` logging with `trace` for cases that users cannot handle
Jun 29, 2022
16b4138
Change warning to trace for absence of Kotlin Lambda class
Jun 29, 2022
734cce8
Null check for merged lambda visitor
Jun 29, 2022
56a90c1
Update LambdaGroupBuilderTest to use updated constructor
Jun 29, 2022
9666d97
All new classes moved to package proguard.optimize.kotlin
Jul 10, 2022
8cc582f
Solve TODO of building empty invoke method code
Jul 10, 2022
64f5038
Merge branch 'Guardsquare:master' into master
heckej Jul 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/continuous_integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ jobs:
- uses: actions/checkout@v2
with:
path: proguard-main
- uses: actions/[email protected]
with:
repository: heckej/proguard-core
path: proguard-core
Comment on lines +16 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- uses: actions/[email protected]
with:
repository: heckej/proguard-core
path: proguard-core

- uses: actions/setup-java@v1
with:
java-version: 8
Expand Down
21 changes: 21 additions & 0 deletions base/src/main/java/proguard/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,27 @@ public class Configuration
*/
public boolean mergeInterfacesAggressively = false;

/**
* An optional output file for listing the lambda to lambdagroup mapping.
* An empty file name means the standard output.
*/
public File printLambdaGroupMapping;

/**
* Specifies whether Kotlin lambda classes can be merged into lambda groups.
*/
public boolean mergeKotlinLambdaClasses;

/**
* Specifies whether Kotlin lambda classes have to be merged before or after the other optimisations.
*/
public boolean lambdaMergingAfterOptimizing;

/**
* Specifies whether Kotlin lambda classes may be merged if they contain unexpected methods.
*/
public boolean mergeLambdaClassesWithUnexpectedMethods;

///////////////////////////////////////////////////////////////////////////
// Obfuscation options.
///////////////////////////////////////////////////////////////////////////
Expand Down
4 changes: 4 additions & 0 deletions base/src/main/java/proguard/ConfigurationConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public class ConfigurationConstants
public static final String ASSUME_VALUES_OPTION = "-assumevalues";
public static final String ALLOW_ACCESS_MODIFICATION_OPTION = "-allowaccessmodification";
public static final String MERGE_INTERFACES_AGGRESSIVELY_OPTION = "-mergeinterfacesaggressively";
public static final String PRINT_LAMBDAGROUP_MAPPING_OPTION = "-printlambdagroupmapping";
public static final String MERGE_KOTLIN_LAMBDA_CLASSES_OPTION = "-mergekotlinlambdaclasses";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the lambda merging to the -optimizations option instead, that will be more consistent e.g.

-optimizations class/merging/kotlinlambda

public static final String LAMBDA_MERGING_AFTER_OPTIMIZE_OPTION = "-lambdamergingafteroptimizing";
heckej marked this conversation as resolved.
Show resolved Hide resolved
public static final String LAMBDA_MERGING_ALLOW_UNEXPECTED_METHODS = "-mergelambdaclasseswithunexpectedmethods";
heckej marked this conversation as resolved.
Show resolved Hide resolved

public static final String DONT_OBFUSCATE_OPTION = "-dontobfuscate";
public static final String PRINT_MAPPING_OPTION = "-printmapping";
Expand Down
4 changes: 4 additions & 0 deletions base/src/main/java/proguard/ConfigurationParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ public void parse(Configuration configuration)
else if (ConfigurationConstants.ASSUME_VALUES_OPTION .startsWith(nextWord)) configuration.assumeValues = parseAssumeClassSpecificationArguments(configuration.assumeValues);
else if (ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION .startsWith(nextWord)) configuration.allowAccessModification = parseNoArgument(true);
else if (ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION .startsWith(nextWord)) configuration.mergeInterfacesAggressively = parseNoArgument(true);
else if (ConfigurationConstants.PRINT_LAMBDAGROUP_MAPPING_OPTION .startsWith(nextWord)) configuration.printLambdaGroupMapping = parseOptionalFile();
else if (ConfigurationConstants.MERGE_KOTLIN_LAMBDA_CLASSES_OPTION .startsWith(nextWord)) configuration.mergeKotlinLambdaClasses = parseNoArgument(true);
else if (ConfigurationConstants.LAMBDA_MERGING_AFTER_OPTIMIZE_OPTION .startsWith(nextWord)) configuration.lambdaMergingAfterOptimizing = parseNoArgument(true);
else if (ConfigurationConstants.LAMBDA_MERGING_ALLOW_UNEXPECTED_METHODS .startsWith(nextWord)) configuration.mergeLambdaClassesWithUnexpectedMethods = parseNoArgument(true);

else if (ConfigurationConstants.DONT_OBFUSCATE_OPTION .startsWith(nextWord)) configuration.obfuscate = parseNoArgument(false);
else if (ConfigurationConstants.PRINT_MAPPING_OPTION .startsWith(nextWord)) configuration.printMapping = parseOptionalFile();
Expand Down
4 changes: 4 additions & 0 deletions base/src/main/java/proguard/ConfigurationWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ public void write(Configuration configuration) throws IOException
writeOption(ConfigurationConstants.OPTIMIZATION_PASSES, configuration.optimizationPasses);
writeOption(ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION, configuration.allowAccessModification);
writeOption(ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION, configuration.mergeInterfacesAggressively);
writeOption(ConfigurationConstants.PRINT_LAMBDAGROUP_MAPPING_OPTION, configuration.printLambdaGroupMapping);
writeOption(ConfigurationConstants.MERGE_KOTLIN_LAMBDA_CLASSES_OPTION, configuration.mergeKotlinLambdaClasses);
writeOption(ConfigurationConstants.LAMBDA_MERGING_AFTER_OPTIMIZE_OPTION, configuration.lambdaMergingAfterOptimizing);
writeOption(ConfigurationConstants.LAMBDA_MERGING_ALLOW_UNEXPECTED_METHODS, configuration.mergeLambdaClassesWithUnexpectedMethods);

writeOption(ConfigurationConstants.DONT_OBFUSCATE_OPTION, !configuration.obfuscate);
writeOption(ConfigurationConstants.PRINT_MAPPING_OPTION, configuration.printMapping);
Expand Down
24 changes: 23 additions & 1 deletion base/src/main/java/proguard/ProGuard.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import proguard.configuration.ConfigurationLoggingAdder;
import proguard.evaluation.IncompleteClassHierarchyException;
import proguard.configuration.InitialStateInfo;
import proguard.io.ExtraDataEntryNameMap;
import proguard.logging.Logging;
import proguard.mark.Marker;
import proguard.obfuscate.NameObfuscationReferenceFixer;
Expand All @@ -39,6 +38,7 @@
import proguard.optimize.LineNumberTrimmer;
import proguard.optimize.Optimizer;
import proguard.optimize.gson.GsonOptimizer;
import proguard.optimize.kotlin.KotlinLambdaMerger;
import proguard.optimize.peephole.LineNumberLinearizer;
import proguard.pass.PassRunner;
import proguard.preverify.*;
Expand Down Expand Up @@ -177,6 +177,11 @@ public void execute() throws Exception
shrink(false);
}

if (configuration.mergeKotlinLambdaClasses && !configuration.lambdaMergingAfterOptimizing)
{
mergeKotlinLambdaClasses();
}

// Create a matcher for filtering optimizations.
StringMatcher filter = configuration.optimizations != null ?
new ListParser(new NameParser()).parse(configuration.optimizations) :
Expand All @@ -194,6 +199,11 @@ public void execute() throws Exception
linearizeLineNumbers();
}

if (configuration.mergeKotlinLambdaClasses && configuration.lambdaMergingAfterOptimizing)
{
mergeKotlinLambdaClasses();
}

if (configuration.obfuscate)
{
obfuscate();
Expand Down Expand Up @@ -424,6 +434,14 @@ private void shrink(boolean afterOptimizer) throws Exception
}
}

/**
* Reduce the size needed to represent Kotlin lambda's.
* The classes that are generated for lambda's with a same structure and from the same package are merged into a group.
*/
private void mergeKotlinLambdaClasses() throws Exception {
passRunner.run(new KotlinLambdaMerger(configuration), appView);
}


/**
* Optimizes usages of the Gson library.
Expand Down Expand Up @@ -452,6 +470,10 @@ private void optimize() throws Exception
{
shrink(true);
}
if (optimizationPass == configuration.optimizationPasses - 2)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Why - 2?
  • Why is this here as well as inside the Optimizer?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to my experience lambda merging has the most impact when it is located in between two last optimisation rounds. That way it benefits from:

  1. any removed lambda classes due to previous optimisations (biggest benefit)
  2. optimised lambda groups due after the last optimisation round (smaller benefit)
    However, when only one round of optimisations is done, lambda merging should be located after that round, as it benefits the most from the removed lambda classes (which don't have to be merged, therefore).
    Any insight on where this is done the best would be helpful :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing that make the above less obvious (in my opinion) is the fact that it cannot be predicted which round will be the one before last round, such that lambda merging is performed at the end of that round.

{
mergeKotlinLambdaClasses();
}
}
}

Expand Down
12 changes: 10 additions & 2 deletions base/src/main/java/proguard/optimize/Optimizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import proguard.optimize.evaluation.*;
import proguard.optimize.evaluation.InstructionUsageMarker;
import proguard.optimize.info.*;
import proguard.optimize.kotlin.KotlinLambdaMerger;
import proguard.optimize.peephole.*;
import proguard.pass.Pass;
import proguard.util.*;
Expand Down Expand Up @@ -195,11 +196,18 @@ public Optimizer(Configuration configuration)


@Override
public void execute(AppView appView) throws IOException
public void execute(AppView appView) throws Exception
{
if (!moreOptimizationsPossible)
{
return;
if (KotlinLambdaMerger.lambdaMergingDone)
{
return;
}
else
{
new KotlinLambdaMerger(configuration).execute(appView);
}
}

// Create a matcher for filtering optimizations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public class ProgramClassOptimizationInfo
private volatile boolean mayBeMerged = true;
private volatile Clazz wrappedClass;
private volatile Clazz targetClass;
private volatile Clazz lambdaGroup;
private volatile int lambdaGroupClassId;


public boolean isKept()
Expand Down Expand Up @@ -250,6 +252,29 @@ public Clazz getTargetClass()
}


public void setLambdaGroup(Clazz lambdaGroup)
{
this.lambdaGroup = lambdaGroup;
}


public Clazz getLambdaGroup()
{
return lambdaGroup;
}

public void setLambdaGroupClassId(int classId)
{
this.lambdaGroupClassId = classId;
}


public int getLambdaGroupClassId()
{
return lambdaGroupClassId;
}


/**
* Merges in the given information of a class that is merged.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package proguard.optimize.kotlin;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the ProGuard header to all the classes?


import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import proguard.Configuration;
import proguard.classfile.*;
import proguard.classfile.attribute.Attribute;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.attribute.ExceptionsAttribute;
import proguard.classfile.attribute.LineNumberTableAttribute;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.editor.ClassBuilder;
import proguard.classfile.editor.ClassEditor;
import proguard.classfile.editor.ConstantPoolEditor;
import proguard.classfile.editor.InstructionSequenceBuilder;
import proguard.classfile.instruction.*;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.util.ClassSuperHierarchyInitializer;
import proguard.classfile.util.ClassUtil;
import proguard.classfile.visitor.*;
import proguard.io.ExtraDataEntryNameMap;
import proguard.optimize.info.ProgramClassOptimizationInfo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class KotlinLambdaClassMerger implements ClassPoolVisitor {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some documentation to the class?


public static final String NAME_LAMBDA_GROUP = "LambdaGroup";
private final ClassVisitor lambdaGroupVisitor;
private final ClassVisitor notMergedLambdaVisitor;
private final Configuration configuration;
private final ClassPool programClassPool;
private final ClassPool libraryClassPool;
private final ExtraDataEntryNameMap extraDataEntryNameMap;
private static final Logger logger = LogManager.getLogger(KotlinLambdaClassMerger.class);

public KotlinLambdaClassMerger(final Configuration configuration,
final ClassPool programClassPool,
final ClassPool libraryClassPool,
final ClassVisitor lambdaGroupVisitor,
final ClassVisitor notMergedLambdaVisitor,
final ExtraDataEntryNameMap extraDataEntryNameMap)
{
this.configuration = configuration;
this.programClassPool = programClassPool;
this.libraryClassPool = libraryClassPool;
this.lambdaGroupVisitor = lambdaGroupVisitor;
this.notMergedLambdaVisitor = notMergedLambdaVisitor;
this.extraDataEntryNameMap = extraDataEntryNameMap;
}

@Override
public void visitClassPool(ClassPool lambdaClassPool)
{
// don't merge lambda's if there is only one or zero
if (lambdaClassPool.size() < 2)
{
if (notMergedLambdaVisitor != null) {
lambdaClassPool.classesAccept(notMergedLambdaVisitor);
}
return;
}

// choose a name for the lambda group
// ensure that the lambda group is in the same package as the classes of the class pool
String lambdaGroupName = getPackagePrefixOfClasses(lambdaClassPool) + NAME_LAMBDA_GROUP;
logger.info("Creating lambda group with name {}", ClassUtil.externalClassName(lambdaGroupName));
heckej marked this conversation as resolved.
Show resolved Hide resolved

// create a lambda group builder
KotlinLambdaGroupBuilder lambdaGroupBuilder = new KotlinLambdaGroupBuilder(lambdaGroupName,
this.configuration,
this.programClassPool,
this.libraryClassPool,
this.extraDataEntryNameMap,
this.notMergedLambdaVisitor);

// visit each lambda of this package to add their implementations to the lambda group
lambdaClassPool.classesAccept(lambdaGroupBuilder);

ProgramClass lambdaGroup = lambdaGroupBuilder.build();

ProgramClassOptimizationInfo.setProgramClassOptimizationInfo(lambdaGroup);
ProgramClassOptimizationInfo optimizationInfo =
ProgramClassOptimizationInfo.getProgramClassOptimizationInfo(lambdaGroup);
optimizationInfo.setLambdaGroup(lambdaGroup);

// let the lambda group visitor visit the newly created lambda group
this.lambdaGroupVisitor.visitProgramClass(lambdaGroup);
}

private static String getPackagePrefixOfClasses(ClassPool classPool)
{
// Assume that all classes in the given class pool are in the same package.
String someClassName = classPool.classNames().next();
return ClassUtil.internalPackagePrefix(someClassName);
}
}
heckej marked this conversation as resolved.
Show resolved Hide resolved
Loading