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

Classes needed for lambda merging in ProGuard #36

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ca6936c
Prevent DominatorCalculator from crashing when a method has an empty …
Masrepus Dec 21, 2021
f0ed6b2
Fix CallResolver to not use unimplemented interface methods as viable…
Masrepus Dec 21, 2021
7f1f0af
Create getter for target method in ConcreteCall
capoz Dec 21, 2021
b626bfc
Remove error logging for abstract call targets
Masrepus Dec 21, 2021
e92a27b
Add ClassMethodFilter from Nadeesh
Dec 28, 2021
2acc29c
Move PackageGrouper to ProGuard-CORE
Jan 5, 2022
e61c38d
Move MethodCopier to ProGuard-CORE
Jan 5, 2022
e39c05e
Create separate & extra tests for PackageGrouper
Jan 5, 2022
5f24388
Make MethodCopier public to be used in KotlinLambdaClassMerger
Jan 5, 2022
3338829
Add Kotlin Unit name, type, INSTANCE field to ClassConstants
Jan 6, 2022
4f74d43
Visitor that visits inner and/or outer class constants of InnerClassInfo
Jan 6, 2022
0d9cb78
Visitor that visits referenced class of ClassConstant
Jan 6, 2022
8c81564
Merge remote-tracking branch 'Guardsquare/master'
Jan 7, 2022
ea25197
ModifiedAllInnerClassesInfoVisitor: revisit inner classes attribute w…
Jan 9, 2022
fc68b07
Merge branch 'Guardsquare:master' into master
heckej Feb 8, 2022
cd4a6a3
Merge branch 'Guardsquare:master' into master
heckej Feb 18, 2022
5550c96
Merge branch 'Guardsquare:master' into master
heckej Feb 24, 2022
9860f9e
Merge branch 'Guardsquare:master' into master
heckej Mar 2, 2022
fcfa75e
New classes: FieldCopier & FieldRenamer
Mar 22, 2022
a33e5da
Use a FieldCopier in the MethodCopier
Mar 22, 2022
bf8155b
FieldRenamer: use descriptor + index as new name
Mar 27, 2022
b2877ea
Don't print warning in FieldCopier
Mar 27, 2022
9d3bb0c
Merge remote-tracking branch 'Guardsquare/master'
Mar 27, 2022
8c82796
ClassConstantToClassVisitor: don't visit referenced class if it is null
May 7, 2022
758be93
PackageGrouper: pretty print package names in log
May 7, 2022
528e7a7
Merge remote-tracking branch 'Guardsquare/master'
May 7, 2022
7821839
Test added for MethodCopier
Jun 6, 2022
b542b9b
Apply ktlint formatting to new test classes
Jun 6, 2022
99065f7
Merge branch 'Guardsquare:master' into master
heckej Jun 6, 2022
4ef0e59
Inner classes of lambda merging moved to ProGuard CORE
Jun 8, 2022
67cde7f
New ConstantVisitor: FieldReferenceFinder
Jun 8, 2022
72a80b9
Move Kotlin specific constants from ClassConstants to KotlinConstants
Jun 29, 2022
7eeff48
Add PGC header to ClassConstantToClassVisitor
Jun 29, 2022
02e8fd0
Open '{' on new line in ClassConstantToClassVisitor
Jun 29, 2022
660a391
Remove redundant ClassMethodFilter
Jun 29, 2022
59f7999
Move visitors to ProGuard
Jun 29, 2022
7d34770
Move ModifiedAllInnerClassesInfoVisitor from PGC to ProGuard
Jun 29, 2022
7441f81
Move InnerClassRemover to proguard.classfile.editor + update docs
Jun 29, 2022
082b3a8
Move FieldCopier to ProGuard
Jun 29, 2022
31f231d
MethodCopierTest moved to ProGuard
Jun 29, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* ProGuardCORE -- library to process Java bytecode.
*
* Copyright (c) 2002-2022 Guardsquare NV
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package proguard.classfile.attribute.visitor;
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 ProGuardCORE header to all the classes?


import proguard.classfile.Clazz;
import proguard.classfile.constant.ClassConstant;
import proguard.classfile.constant.Constant;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.visitor.ClassVisitor;
import proguard.classfile.visitor.MemberVisitor;

/**
* This {@link ConstantVisitor} lets a given {@link ClassVisitor} visit the classes that are referenced by the visited
* class constants.
*
* @author Joren Van Hecke
*/
public class ClassConstantToClassVisitor implements ConstantVisitor
{

private final ClassVisitor classVisitor;

public ClassConstantToClassVisitor(ClassVisitor classVisitor)
{
this.classVisitor = classVisitor;
}

public void visitAnyConstant(Clazz clazz, Constant constant) {}

@Override
public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
{
if (this.classVisitor != null && classConstant.referencedClass != null)
{
classConstant.referencedClass.accept(this.classVisitor);
}
}
}
77 changes: 77 additions & 0 deletions src/main/java/proguard/classfile/editor/InnerClassRemover.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* ProGuardCORE -- library to process Java bytecode.
*
* Copyright (c) 2002-2022 Guardsquare NV
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package proguard.classfile.editor;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import proguard.classfile.Clazz;
import proguard.classfile.attribute.Attribute;
import proguard.classfile.attribute.InnerClassesAttribute;
import proguard.classfile.attribute.InnerClassesInfo;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.attribute.visitor.InnerClassesInfoVisitor;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
* This {@link AttributeVisitor} and {@link InnerClassesInfoVisitor} removes a given {@link Clazz} from all visited
* {@link InnerClassesAttribute}s.
*
* @author Joren Van Hecke
*/
public class InnerClassRemover implements AttributeVisitor, InnerClassesInfoVisitor
{
private final Clazz classToBeRemoved;
private final Set<InnerClassesInfo> innerClassesEntriesToBeRemoved = new HashSet<>();
private static final Logger logger = LogManager.getLogger(InnerClassRemover.class);

public InnerClassRemover(Clazz clazz)
{
this.classToBeRemoved = clazz;
}

@Override
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}

@Override
public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
{
innerClassesAttribute.innerClassEntriesAccept(clazz, this);
InnerClassesAttributeEditor editor = new InnerClassesAttributeEditor(innerClassesAttribute);
logger.trace("{} inner class entries are removed from class {}",
innerClassesEntriesToBeRemoved.size(), clazz);
for (InnerClassesInfo entry : innerClassesEntriesToBeRemoved)
{
editor.removeInnerClassesInfo(entry);
}
}

@Override
public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
{
String innerClassName = clazz.getClassName(innerClassesInfo.u2innerClassIndex);
if (Objects.equals(innerClassName, this.classToBeRemoved.getName()))
{
logger.trace("Removing inner classes entry of class {} enqueued to be removed from class {}",
innerClassName, clazz);
innerClassesEntriesToBeRemoved.add(innerClassesInfo);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class KotlinConstants
public static final String TYPE_KOTLIN_METADATA = "Lkotlin/Metadata;";
public static final String NAME_KOTLIN_ANY = "kotlin/Any";
public static final String NAME_KOTLIN_UNIT = "kotlin/Unit";
public static final String TYPE_KOTLIN_UNIT = "Lkotlin/Unit;";
public static final String NAME_KOTLIN_ENUM = "kotlin/Enum";
public static final String NAME_KOTLIN_FUNCTION = "kotlin/Function"; // kotlin/Function and also kotlin/FunctionN
public static final String NAME_KOTLIN_EXTENSION_FUNCTION = "kotlin/ExtensionFunctionType";
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/proguard/classfile/visitor/ClassReferenceFinder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package proguard.classfile.visitor;

import proguard.classfile.Clazz;
import proguard.classfile.constant.ClassConstant;
import proguard.classfile.constant.Constant;
import proguard.classfile.constant.visitor.ConstantVisitor;

public class ClassReferenceFinder implements ConstantVisitor
Copy link
Collaborator

Choose a reason for hiding this comment

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

This might be replacable by the following Filter class which can delegate to another ClassVisitor if the the class is referenced.

It's extracted from proguard.backport.StaticInterfaceMethodConverter.MyReferencedClassFilter in ProGuard.

Usable like the following:

        this.programClassPool.classesAccept(
                new ClassNameFilter(regularExpression,
                new ReferencedClassFilter(
                    lambdaClass,
                    enclosingMethodUpdater)));

But I see that currentLambdaClass gets updated, so might not work. Can also be used with a ClassCounter:

ClassCounter counter = new ClassCounter();
programClass.accept(new ReferencedClassFilter(currentLambdaClass, counter))
package proguard.classfile.visitor;

import proguard.classfile.*;
import proguard.classfile.constant.*;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.visitor.ClassVisitor;

/**
 * This ClassVisitor delegates its visits to classes that
 * reference a given class via any RefConstant.
 */
public class ReferencedClassFilter
    implements ClassVisitor,
               ConstantVisitor
{
    private final Clazz        referencedClass;
    private final ClassVisitor classVisitor;

    private boolean referenceClassFound;


    public ReferencedClassFilter(Clazz referencedClass,
                                 ClassVisitor classVisitor)
    {
        this.referencedClass = referencedClass;
        this.classVisitor = classVisitor;
    }


    // Implementations for ClassVisitor.


    @Override
    public void visitAnyClass(Clazz clazz) {}


    @Override
    public void visitProgramClass(ProgramClass programClass)
    {
        referenceClassFound = false;
        programClass.constantPoolEntriesAccept(this);

        if (referenceClassFound)
        {
            programClass.accept(classVisitor);
        }
    }


    // Implementations for ConstantVisitor.


    public void visitAnyConstant(Clazz clazz, Constant constant) {}


    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
    {
        if (refConstant.referencedClass == referencedClass)
        {
            referenceClassFound = true;
        }
    }
}

{
private final Clazz referencedClass;
private boolean classIsReferenced = false;

public ClassReferenceFinder(Clazz referencedClass)
{
this.referencedClass = referencedClass;
}

public void visitAnyConstant(Clazz clazz, Constant constant) {}

public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
{
if (classConstant.referencedClass != null && classConstant.referencedClass.equals(referencedClass))
{
this.classIsReferenced = true;
}
}

public boolean classReferenceFound()
{
return this.classIsReferenced;
}
}
29 changes: 29 additions & 0 deletions src/main/java/proguard/classfile/visitor/CodeSizeCounter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package proguard.classfile.visitor;

import proguard.classfile.Clazz;
import proguard.classfile.Method;
import proguard.classfile.attribute.Attribute;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.attribute.visitor.AttributeVisitor;

public class CodeSizeCounter
implements AttributeVisitor
{
private int totalCodeSize = 0;

// Implementations for AttributeVisitor

@Override
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}

@Override
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
totalCodeSize += codeAttribute.u4codeLength;
}

public int getCount()
{
return totalCodeSize;
}
}
Comment on lines +9 to +29
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
public class CodeSizeCounter
implements AttributeVisitor
{
private int totalCodeSize = 0;
// Implementations for AttributeVisitor
@Override
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
@Override
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
totalCodeSize += codeAttribute.u4codeLength;
}
public int getCount()
{
return totalCodeSize;
}
}
public class CodeAttributeSizeCounter
implements Counter,
AttributeVisitor
{
private int size = 0;
// Implementations for Counter.
public int getCount()
{
return size;
}
// Implementations for AttributeVisitor.
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
size += codeAttribute.u4codeLength;
}
}

We have a class like this already but it's not in ProGuardCORE at the moment. There's already the Counter interface to use with these counting visitors.

Can you move this also into the attribute/visitor package, since it's an AttributeVisitor?

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package proguard.classfile.visitor;

import proguard.classfile.Clazz;
import proguard.classfile.constant.Constant;
import proguard.classfile.constant.Utf8Constant;
import proguard.classfile.constant.visitor.ConstantVisitor;

public class DescriptorTypeUpdater implements ConstantVisitor
{
private final String originalType;
private final String replacingType;
public DescriptorTypeUpdater(String originalType, String replacingType)
{
this.originalType = originalType;
this.replacingType = replacingType;
}

@Override
public void visitAnyConstant(Clazz clazz, Constant constant) {}

@Override
public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) {
utf8Constant.setString(utf8Constant.getString().replace(originalType, replacingType));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here we should be using a ConstantPoolEditor to add a new constant. You can use a ConstantPoolShrinker after to remove unused ones.

        programMember.u2nameIndex =
            new ConstantPoolEditor(programClass).addUtf8Constant(newName);

}
}
28 changes: 28 additions & 0 deletions src/test/kotlin/testutils/MatchDetector.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package testutils

import proguard.classfile.Clazz
import proguard.classfile.Method
import proguard.classfile.attribute.CodeAttribute
import proguard.classfile.instruction.Instruction
import proguard.classfile.instruction.visitor.InstructionVisitor
import proguard.classfile.util.InstructionSequenceMatcher

class MatchDetector(val matcher: InstructionSequenceMatcher, vararg val arguments: Int) : InstructionVisitor {
var matchIsFound = false
var matchedArguments = IntArray(arguments.size)

override fun visitAnyInstruction(
clazz: Clazz,
method: Method,
codeAttribute: CodeAttribute,
offset: Int,
instruction: Instruction
) {
println(instruction.toString(clazz, offset))
instruction.accept(clazz, method, codeAttribute, offset, matcher)
if (matcher.isMatching()) {
matchIsFound = true
matchedArguments = matcher.matchedArguments(arguments)
}
}
}