From ca6936c700853eb420d1b9e84606c7e9762c784b Mon Sep 17 00:00:00 2001 From: Samuel Hopstock Date: Tue, 21 Dec 2021 11:20:27 +0100 Subject: [PATCH 01/32] Prevent DominatorCalculator from crashing when a method has an empty code attribute Summary: Methods can sometimes have empty code attributes, if Dex2Pro skips them due to some failure. Unfortunately, this made the DominatorCalculator crash with an ArrayIndexOutOfBoundsException. This has been fixed by making the analysis skip empty code attributes. Test Plan: New unit test Reviewers: james.hamilton, dennis.titze Reviewed By: dennis.titze Differential Revision: http://phabricator.guardsquare.com/D10236 --- docs/md/releasenotes.md | 6 +++++ .../analysis/DominatorCalculator.java | 5 +++++ .../analysis/DominatorCalculatorTest.kt | 22 +++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/docs/md/releasenotes.md b/docs/md/releasenotes.md index ecad5d959..e28bd53ad 100644 --- a/docs/md/releasenotes.md +++ b/docs/md/releasenotes.md @@ -1,3 +1,9 @@ +## Version 8.0.5 + +### Bug fixes + +- Make the `DominatorCalculator` skip methods with an empty `CodeAttribute`. + ## Version 8.0.4 ### Improved diff --git a/src/main/java/proguard/analysis/DominatorCalculator.java b/src/main/java/proguard/analysis/DominatorCalculator.java index f77904f19..a60c97de7 100644 --- a/src/main/java/proguard/analysis/DominatorCalculator.java +++ b/src/main/java/proguard/analysis/DominatorCalculator.java @@ -188,6 +188,11 @@ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAtt */ private void run(CodeAttribute codeAttribute) { + if (codeAttribute.u4codeLength == 0) + { + // Skip the method if it doesn't contain any code (e.g. if it was skipped during Dex2Pro conversion). + return; + } LinkedHashSet workList = new LinkedHashSet<>(); workList.add(0); diff --git a/src/test/kotlin/proguard/analysis/DominatorCalculatorTest.kt b/src/test/kotlin/proguard/analysis/DominatorCalculatorTest.kt index aa14dd563..111e3d0f8 100644 --- a/src/test/kotlin/proguard/analysis/DominatorCalculatorTest.kt +++ b/src/test/kotlin/proguard/analysis/DominatorCalculatorTest.kt @@ -18,6 +18,7 @@ package proguard.analysis +import io.kotest.assertions.throwables.shouldNotThrowAny import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.shouldBe @@ -421,4 +422,25 @@ class DominatorCalculatorTest : FreeSpec({ calculator.dominates(0, 7) } } + + "Empty CodeAttribute" - { + val code = byteArrayOf() + val clazz = NamedClass("Test") + val method = NamedMember("", "()V") + val codeAttribute = CodeAttribute(0, 1, 0, code.size, code) + + val calculator = DominatorCalculator() + + "Analysis does not crash" { + shouldNotThrowAny { + codeAttribute.accept(clazz, method, calculator) + } + } + + "All offsets are unknown" { + shouldThrow { + calculator.dominates(0, 1) + } + } + } }) From f0ed6b2a18af97ec62c995d7fa2fd52bec82957d Mon Sep 17 00:00:00 2001 From: Samuel Hopstock Date: Tue, 21 Dec 2021 14:51:40 +0100 Subject: [PATCH 02/32] Fix CallResolver to not use unimplemented interface methods as viable targets Summary: When searching for call targets in superinterfaces the call resolver previously didn't enforce that any potential target must be non-abstract. Also, if any other resolution path results in a call to an abstract method, this is now logged as an error to catch bugs earlier. Test Plan: Existing tests Reviewers: james.hamilton, carlo.alberto.pozzoli, dennis.titze Reviewed By: dennis.titze Differential Revision: http://phabricator.guardsquare.com/D10245 --- docs/md/releasenotes.md | 1 + src/main/java/proguard/analysis/CallResolver.java | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/md/releasenotes.md b/docs/md/releasenotes.md index e28bd53ad..8a3f654de 100644 --- a/docs/md/releasenotes.md +++ b/docs/md/releasenotes.md @@ -2,6 +2,7 @@ ### Bug fixes +- Fix `CallResolver` erroneously creating call edges to unimplemented interface methods. - Make the `DominatorCalculator` skip methods with an empty `CodeAttribute`. ## Version 8.0.4 diff --git a/src/main/java/proguard/analysis/CallResolver.java b/src/main/java/proguard/analysis/CallResolver.java index fcf0f5c86..7b27ec90c 100644 --- a/src/main/java/proguard/analysis/CallResolver.java +++ b/src/main/java/proguard/analysis/CallResolver.java @@ -372,6 +372,10 @@ private Call instantiateCall(CodeLocation location, controlFlowDependent, runtimeTypeDependent); Metrics.increaseCount(MetricType.CONCRETE_CALL); + if ((method.getAccessFlags() & AccessConstants.ABSTRACT) != 0) + { + log.error("Resolved call target is an abstract method: {}", call); + } } } return call; @@ -768,7 +772,7 @@ private Optional resolveFromSuperclasses(Clazz start, String name, Strin while (curr != null) { Method targetMethod = curr.findMethod(name, descriptor); - if (targetMethod != null) + if (targetMethod != null && (targetMethod.getAccessFlags() & AccessConstants.ABSTRACT) == 0) { return Optional.of(curr.getName()); } @@ -806,7 +810,9 @@ private Set resolveFromSuperinterfaces(Clazz start, String name, String .filter(i -> { Method m = i.findMethod(name, descriptor); - return m != null && (m.getAccessFlags() & (AccessConstants.PRIVATE | AccessConstants.STATIC)) == 0; + return m != null && (m.getAccessFlags() & (AccessConstants.PRIVATE + | AccessConstants.STATIC + | AccessConstants.ABSTRACT)) == 0; }) .collect(Collectors.toSet()); From 7f1f0af3afa284354be5aa8cb5beee1ded4f3458 Mon Sep 17 00:00:00 2001 From: Carlo Alberto Pozzoli Date: Tue, 21 Dec 2021 16:22:02 +0100 Subject: [PATCH 03/32] Create getter for target method in ConcreteCall Test Plan: existing tests Reviewers: samuel.hopstock Reviewed By: samuel.hopstock Differential Revision: http://phabricator.guardsquare.com/D10254 --- .../analysis/datastructure/callgraph/ConcreteCall.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/proguard/analysis/datastructure/callgraph/ConcreteCall.java b/src/main/java/proguard/analysis/datastructure/callgraph/ConcreteCall.java index 52df4771a..bcf592f34 100644 --- a/src/main/java/proguard/analysis/datastructure/callgraph/ConcreteCall.java +++ b/src/main/java/proguard/analysis/datastructure/callgraph/ConcreteCall.java @@ -88,6 +88,11 @@ public Clazz getTargetClass() return targetClass; } + public Method getTargetMethod() + { + return target; + } + @Override public boolean equals(Object o) { From b626bfca1a6d170d54f8f052f2bb4865cd8b3d93 Mon Sep 17 00:00:00 2001 From: Samuel Hopstock Date: Tue, 21 Dec 2021 16:06:45 +0100 Subject: [PATCH 04/32] Remove error logging for abstract call targets Summary: Previously an error was logged when the call target turned out to be an abstract method. Unfortunately, things like `myList.iterator().hasNext()` validly target an abstract method, which doesn't warrant an error log message. Keeping the logging in would create tons of log spam. Test Plan: N/A Reviewers: dennis.titze Reviewed By: dennis.titze Differential Revision: http://phabricator.guardsquare.com/D10252 --- src/main/java/proguard/analysis/CallResolver.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/proguard/analysis/CallResolver.java b/src/main/java/proguard/analysis/CallResolver.java index 7b27ec90c..7c2f843d3 100644 --- a/src/main/java/proguard/analysis/CallResolver.java +++ b/src/main/java/proguard/analysis/CallResolver.java @@ -374,7 +374,7 @@ private Call instantiateCall(CodeLocation location, Metrics.increaseCount(MetricType.CONCRETE_CALL); if ((method.getAccessFlags() & AccessConstants.ABSTRACT) != 0) { - log.error("Resolved call target is an abstract method: {}", call); + Metrics.increaseCount(MetricType.CALL_TO_ABSTRACT_METHOD); } } } @@ -873,7 +873,8 @@ public enum MetricType PARTIAL_EVALUATOR_EXCEPTION, PARTIAL_EVALUATOR_VALUE_IMPRECISE, SYMBOLIC_CALL, - CONCRETE_CALL + CONCRETE_CALL, + CALL_TO_ABSTRACT_METHOD } public static final Map counts = new TreeMap<>(); From e92a27b8d3814985904840ddd2e5c5a0cfb25a84 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Tue, 28 Dec 2021 19:27:55 +0100 Subject: [PATCH 05/32] Add ClassMethodFilter from Nadeesh --- .../classfile/visitor/ClassMethodFilter.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/main/java/proguard/classfile/visitor/ClassMethodFilter.java diff --git a/src/main/java/proguard/classfile/visitor/ClassMethodFilter.java b/src/main/java/proguard/classfile/visitor/ClassMethodFilter.java new file mode 100644 index 000000000..7d3090ddd --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/ClassMethodFilter.java @@ -0,0 +1,52 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2021 Guardsquare NV + */ + +package proguard.classfile.visitor; + +import proguard.classfile.*; + +/** + * This {@link ClassVisitor} delegates its visits to given + * acceptVisitor when the visited class has method with + * given name and descriptor and delegates to rejectVisitor when + * none of the class methods match. + */ +public class ClassMethodFilter +implements ClassVisitor +{ + private final String methodName; + private final String descriptor; + private final ClassVisitor acceptVisitor; + private final ClassVisitor rejectVisitor; + + public ClassMethodFilter(String methodName, + String descriptor, + ClassVisitor acceptVisitor, + ClassVisitor rejectVisitor) + { + this.methodName = methodName; + this.descriptor = descriptor; + this.acceptVisitor = acceptVisitor; + this.rejectVisitor = rejectVisitor; + } + + @Override + public void visitAnyClass(Clazz clazz) + { + ClassVisitor delegateVisitor = delegateVisitor(clazz); + if (delegateVisitor != null) + { + delegateVisitor.visitAnyClass(clazz); + } + } + + private ClassVisitor delegateVisitor(Clazz clazz) + { + Method method = clazz.findMethod(methodName, descriptor); + return method != null ? acceptVisitor : rejectVisitor; + } +} \ No newline at end of file From 2acc29c72fe519de023bd085cf2c35f725b99de4 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 5 Jan 2022 16:54:01 +0100 Subject: [PATCH 06/32] Move PackageGrouper to ProGuard-CORE --- .../classfile/visitor/PackageGrouper.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/proguard/classfile/visitor/PackageGrouper.java diff --git a/src/main/java/proguard/classfile/visitor/PackageGrouper.java b/src/main/java/proguard/classfile/visitor/PackageGrouper.java new file mode 100644 index 000000000..c0261dd27 --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/PackageGrouper.java @@ -0,0 +1,65 @@ +package proguard.classfile.visitor; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import proguard.classfile.ClassPool; +import proguard.classfile.Clazz; +import proguard.classfile.util.ClassUtil; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class + */ +public class PackageGrouper implements ClassVisitor { + + private final Map packageClassPools = new HashMap<>(); + private static final Logger logger = LogManager.getLogger(PackageGrouper.class); + + @Override + public void visitAnyClass(Clazz clazz) + { + String classPackageName = ClassUtil.internalPackageName(clazz.getName()); + // or + // String classPackageName = ClassUtil.internalPackageName(clazz.getName()); + if (!packageClassPools.containsKey(classPackageName)) + { + logger.info("New package found: {}", classPackageName); + packageClassPools.put(classPackageName, new ClassPool()); + } + packageClassPools.get(classPackageName).addClass(clazz); + } + + public int size() + { + return packageClassPools.size(); + } + + public boolean containsPackage(String packageName) + { + return packageClassPools.containsKey(packageName); + } + + public Iterable packageNames() + { + return packageClassPools.keySet(); + } + + public void packagesAccept(ClassPoolVisitor classPoolVisitor) + { + for (ClassPool packageClassPool : packageClassPools.values()) + { + classPoolVisitor.visitClassPool(packageClassPool); + } + } + + public void packageAccept(String packageName, ClassPoolVisitor classPoolVisitor) + { + ClassPool packageClassPool = this.packageClassPools.get(packageName); + if (packageClassPool != null) + { + classPoolVisitor.visitClassPool(packageClassPool); + } + } +} From e61c38d799369d95c4e174ac0da0b056b8bb3a4a Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 5 Jan 2022 16:54:15 +0100 Subject: [PATCH 07/32] Move MethodCopier to ProGuard-CORE --- .../classfile/visitor/MethodCopier.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/main/java/proguard/classfile/visitor/MethodCopier.java diff --git a/src/main/java/proguard/classfile/visitor/MethodCopier.java b/src/main/java/proguard/classfile/visitor/MethodCopier.java new file mode 100644 index 000000000..69205731f --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/MethodCopier.java @@ -0,0 +1,87 @@ +package proguard.classfile.visitor; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import proguard.classfile.*; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.ConstantInstruction; +import proguard.classfile.instruction.Instruction; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +class MethodCopier implements MemberVisitor, AttributeVisitor, InstructionVisitor +{ + private final ProgramClass destinationClass; + private final ClassBuilder classBuilder; + private final String newMethodNamePrefix; + private final ConstantAdder constantAdder; + private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); + private final ExceptionInfoAdder exceptionInfoAdder; + private int methodCounter = 0; + private static final Logger logger = LogManager.getLogger(MethodCopier.class); + + + public MethodCopier(ProgramClass destinationClass, String newMethodNamePrefix) + { + this.destinationClass = destinationClass; + this.classBuilder = new ClassBuilder(destinationClass); + this.newMethodNamePrefix = newMethodNamePrefix; + this.constantAdder = new ConstantAdder(destinationClass); + this.exceptionInfoAdder = new ExceptionInfoAdder(this.destinationClass, this.codeAttributeComposer); + } + + private int getNewMethodIndex() + { + int methodIndex = this.methodCounter; + this.methodCounter++; + return methodIndex; + } + + @Override + public void visitAnyMember(Clazz clazz, Member member) { + } + + @Override + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { + codeAttributeComposer.reset(); + programMethod.attributesAccept(programClass, this); + int methodIndex = getNewMethodIndex(); + String newMethodName = newMethodNamePrefix; + if (methodIndex > 1) + { + newMethodName += "$" + methodIndex; + } + ProgramMethod newMethod = classBuilder.addAndReturnMethod(AccessConstants.PRIVATE, newMethodName, programMethod.getDescriptor(programClass)); + codeAttributeComposer.addCodeAttribute(this.destinationClass, newMethod); + } + + @Override + public void visitAnyAttribute(Clazz clazz, Attribute attribute) { + } + + @Override + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { + codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength); + // copy code and exceptions + codeAttribute.instructionsAccept(clazz, method, this); + codeAttribute.exceptionsAccept(clazz, method, this.exceptionInfoAdder); + codeAttribute.attributesAccept(clazz, method, this); + codeAttributeComposer.endCodeFragment(); + } + + @Override + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) { + // copy instruction + codeAttributeComposer.appendInstruction(offset, instruction); + } + + @Override + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) { + // ensure the referenced constant is in the constant pool at the correct index + constantInstruction.constantIndex = this.constantAdder.addConstant(clazz, constantInstruction.constantIndex); + // copy instruction + codeAttributeComposer.appendInstruction(offset, constantInstruction); + } +} From e39c05eda73c66425b5c668a898a97897df7c65e Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 5 Jan 2022 16:55:02 +0100 Subject: [PATCH 08/32] Create separate & extra tests for PackageGrouper --- .../classfile/visitor/PackageGrouperTest.kt | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/test/kotlin/proguard/classfile/visitor/PackageGrouperTest.kt diff --git a/src/test/kotlin/proguard/classfile/visitor/PackageGrouperTest.kt b/src/test/kotlin/proguard/classfile/visitor/PackageGrouperTest.kt new file mode 100644 index 000000000..7b659b761 --- /dev/null +++ b/src/test/kotlin/proguard/classfile/visitor/PackageGrouperTest.kt @@ -0,0 +1,98 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2021 Guardsquare NV + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package proguard.classfile.visitor + +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.collections.shouldBeIn +import io.kotest.matchers.shouldBe +import proguard.classfile.ClassPool +import proguard.classfile.util.ClassUtil +import testutils.ClassPoolBuilder +import testutils.KotlinSource + +class PackageGrouperTest : FreeSpec({ + + val (programClassPool, _) = ClassPoolBuilder.fromSource( + KotlinSource( + "Test.kt", + """ + package app.package1 + fun main(index: Int) { + val lambda1 = { + println("Lambda1") + } + lambda1() + } + """.trimIndent() + ), + KotlinSource( + "Test2.kt", + """ + package app.package2 + fun main() { + val lambda2 = { println("Lambda2") } + lambda2() + } + """.trimIndent() + ) + ) + + "Given a PackageGrouper" - { + val grouper = PackageGrouper() + "When the grouper is applied to classes of different packages" - { + programClassPool.classesAccept(grouper) + "Then the amount packages of packages found by the grouper equals the amount of packages in the class pool" { + grouper.size() shouldBe 2 + } + "Then the grouper has found the packages that are in the class pool" { + grouper.containsPackage("app/package1") shouldBe true + grouper.containsPackage("app/package2") shouldBe true + } + "Then the grouper does not contain other packages, except for those from the class pool" { + grouper.packageNames().forEach { + it shouldBeIn arrayListOf("app/package2", "app/package1") + } + } + "Then the union of classes in the packages of the package grouper contain equals the class pool" { + val grouperCompleteClassPool = ClassPool() + grouper.packagesAccept { packageClassPool -> + packageClassPool.classes().forEach { clazz -> + programClassPool.contains(clazz) + grouperCompleteClassPool.addClass(clazz) + } + } + programClassPool.classes().forEach { clazz -> + grouperCompleteClassPool.contains(clazz) + } + } + "Then the classes of a package class pool belong to the respective package" { + grouper.packageNames().forEach { packageName -> + grouper.packageAccept(packageName) { packageClassPool -> + packageClassPool.classesAccept { clazz -> + ClassUtil.internalPackageName(clazz.name) shouldBe packageName + } + } + } + } + } + } +}) From 5f24388c433b387726d3b5373297cc260b9c4972 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 5 Jan 2022 18:09:38 +0100 Subject: [PATCH 09/32] Make MethodCopier public to be used in KotlinLambdaClassMerger --- src/main/java/proguard/classfile/visitor/MethodCopier.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/proguard/classfile/visitor/MethodCopier.java b/src/main/java/proguard/classfile/visitor/MethodCopier.java index 69205731f..e669705eb 100644 --- a/src/main/java/proguard/classfile/visitor/MethodCopier.java +++ b/src/main/java/proguard/classfile/visitor/MethodCopier.java @@ -11,7 +11,7 @@ import proguard.classfile.instruction.Instruction; import proguard.classfile.instruction.visitor.InstructionVisitor; -class MethodCopier implements MemberVisitor, AttributeVisitor, InstructionVisitor +public class MethodCopier implements MemberVisitor, AttributeVisitor, InstructionVisitor { private final ProgramClass destinationClass; private final ClassBuilder classBuilder; From 3338829ac13d9c183889a7a15c640e21b143ed41 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Thu, 6 Jan 2022 17:20:49 +0100 Subject: [PATCH 10/32] Add Kotlin Unit name, type, INSTANCE field to ClassConstants --- src/main/java/proguard/classfile/ClassConstants.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/proguard/classfile/ClassConstants.java b/src/main/java/proguard/classfile/ClassConstants.java index 0afe35810..15771265f 100644 --- a/src/main/java/proguard/classfile/ClassConstants.java +++ b/src/main/java/proguard/classfile/ClassConstants.java @@ -105,6 +105,10 @@ public class ClassConstants public static final String NAME_ANDROID_WEBKIT_WEB_VIEW = "android/webkit/WebView"; public static final String NAME_ANDROID_SUPPORT_V4_APP_FRAGMENT = "android/support/v4/app/Fragment"; + public static final String NAME_KOTLIN_UNIT = "kotlin/Unit"; + public static final String TYPE_KOTLIN_UNIT = "Lkotlin/Unit;"; + public static final String FIELD_NAME_KOTLIN_UNIT_INSTANCE = "INSTANCE"; + public static final String NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicIntegerFieldUpdater"; public static final String NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicLongFieldUpdater"; public static final String NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicReferenceFieldUpdater"; From 4f74d434755763f94557fbf54f924de7bce03e54 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Thu, 6 Jan 2022 17:21:39 +0100 Subject: [PATCH 11/32] Visitor that visits inner and/or outer class constants of InnerClassInfo --- .../InnerClassInfoClassConstantVisitor.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/proguard/classfile/attribute/visitor/InnerClassInfoClassConstantVisitor.java diff --git a/src/main/java/proguard/classfile/attribute/visitor/InnerClassInfoClassConstantVisitor.java b/src/main/java/proguard/classfile/attribute/visitor/InnerClassInfoClassConstantVisitor.java new file mode 100644 index 000000000..b3020b62d --- /dev/null +++ b/src/main/java/proguard/classfile/attribute/visitor/InnerClassInfoClassConstantVisitor.java @@ -0,0 +1,30 @@ +package proguard.classfile.attribute.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.ProgramClass; +import proguard.classfile.attribute.InnerClassesInfo; +import proguard.classfile.constant.visitor.ConstantVisitor; + +public class InnerClassInfoClassConstantVisitor implements InnerClassesInfoVisitor { + + private final ConstantVisitor innerClassConstantVisitor; + private final ConstantVisitor outerClassConstantVisitor; + + public InnerClassInfoClassConstantVisitor(ConstantVisitor innerClassConstantVisitor, ConstantVisitor outerClassConstantVisitor) + { + this.innerClassConstantVisitor = innerClassConstantVisitor; + this.outerClassConstantVisitor = outerClassConstantVisitor; + } + + @Override + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) { + if (this.innerClassConstantVisitor != null) + { + innerClassesInfo.innerClassConstantAccept(clazz, this.innerClassConstantVisitor); + } + if (this.outerClassConstantVisitor != null) + { + innerClassesInfo.outerClassConstantAccept(clazz, this.outerClassConstantVisitor); + } + } +} From 0d9cb783f6deac364b16347eb3abbad34130d32b Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Thu, 6 Jan 2022 17:22:02 +0100 Subject: [PATCH 12/32] Visitor that visits referenced class of ClassConstant --- .../visitor/ClassConstantToClassVisitor.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java diff --git a/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java b/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java new file mode 100644 index 000000000..7707434b7 --- /dev/null +++ b/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java @@ -0,0 +1,24 @@ +package proguard.classfile.attribute.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.ClassVisitor; + +public class ClassConstantToClassVisitor implements ConstantVisitor { + + private final ClassVisitor classVisitor; + + public ClassConstantToClassVisitor(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + @Override + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { + if (this.classVisitor != null) + { + classConstant.referencedClass.accept(this.classVisitor); + } + } +} From ea25197d7d4ad6e19e788ac89bfccca047d10944 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Sun, 9 Jan 2022 13:07:31 +0100 Subject: [PATCH 13/32] ModifiedAllInnerClassesInfoVisitor: revisit inner classes attribute when count has changed New classes: ModifiedAllInnerClassesInfoVisitor --- .../ModifiedAllInnerClassesInfoVisitor.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/proguard/classfile/attribute/visitor/ModifiedAllInnerClassesInfoVisitor.java diff --git a/src/main/java/proguard/classfile/attribute/visitor/ModifiedAllInnerClassesInfoVisitor.java b/src/main/java/proguard/classfile/attribute/visitor/ModifiedAllInnerClassesInfoVisitor.java new file mode 100644 index 000000000..310241800 --- /dev/null +++ b/src/main/java/proguard/classfile/attribute/visitor/ModifiedAllInnerClassesInfoVisitor.java @@ -0,0 +1,25 @@ +package proguard.classfile.attribute.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.attribute.InnerClassesAttribute; + +/** + * This {@link AllInnerClassesInfoVisitor} revisits each {@link InnerClassesAttribute} everytime its amount of + * referenced classes has been modified in the meantime. + */ +public class ModifiedAllInnerClassesInfoVisitor extends AllInnerClassesInfoVisitor { + + public ModifiedAllInnerClassesInfoVisitor(InnerClassesInfoVisitor innerClassesInfoVisitor) { + super(innerClassesInfoVisitor); + } + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + int originalClassesCount = -1; + while (originalClassesCount != innerClassesAttribute.u2classesCount) + { + originalClassesCount = innerClassesAttribute.u2classesCount; + super.visitInnerClassesAttribute(clazz, innerClassesAttribute); + } + } +} From fcfa75e62582bc3d5ae115a6571aa35ec78363e5 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Tue, 22 Mar 2022 22:48:39 +0100 Subject: [PATCH 14/32] New classes: FieldCopier & FieldRenamer --- .../classfile/visitor/FieldCopier.java | 80 +++++++++++++++++++ .../classfile/visitor/FieldRenamer.java | 46 +++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/main/java/proguard/classfile/visitor/FieldCopier.java create mode 100644 src/main/java/proguard/classfile/visitor/FieldRenamer.java diff --git a/src/main/java/proguard/classfile/visitor/FieldCopier.java b/src/main/java/proguard/classfile/visitor/FieldCopier.java new file mode 100644 index 000000000..a1dd5d49c --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/FieldCopier.java @@ -0,0 +1,80 @@ +package proguard.classfile.visitor; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import proguard.classfile.*; +import proguard.classfile.constant.FieldrefConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.ClassBuilder; +import proguard.classfile.editor.ClassEditor; + +import java.util.Objects; + +public class FieldCopier implements MemberVisitor, ConstantVisitor { + + private final ClassBuilder classBuilder; + private final ClassEditor classEditor; + private final FieldRenamer fieldRenamer; + private ProgramField lastCopiedField; + private boolean hasCopiedField = false; + private static final Logger logger = LogManager.getLogger(FieldCopier.class); + + public FieldCopier(ClassBuilder builder, FieldRenamer renamer) + { + this.classBuilder = builder; + this.classEditor = new ClassEditor(builder.getProgramClass()); + this.fieldRenamer = renamer; + } + + @Override + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + String fieldName = programField.getName(programClass); + if (this.fieldRenamer != null) + { + fieldName = fieldRenamer.getNextFieldName(); + } + + String fieldDescriptor = programField.getDescriptor(programClass); + Field oldField =classBuilder.getProgramClass().findField(fieldName, null); + Field oldFieldSameDescriptor = classBuilder.getProgramClass().findField(fieldName, fieldDescriptor); + if (oldField != null && oldFieldSameDescriptor == null) + { + String oldFieldDescriptor = oldField.getDescriptor(classBuilder.getProgramClass()); + logger.warn("Field " + fieldName + " already exists in class " + classBuilder.getProgramClass() + " with different descriptor: " + oldFieldDescriptor + " <-> " + fieldDescriptor + ". The field will be duplicated with different descriptors."); + // Merge the field types: generalise to a common super type + //fieldDescriptor = ClassConstants.TYPE_JAVA_LANG_OBJECT; + } + else if (oldFieldSameDescriptor != null) + { + this.classEditor.removeField(oldFieldSameDescriptor); + } + ProgramField copiedField = classBuilder.addAndReturnField(programField.u2accessFlags, fieldName, fieldDescriptor); + if (this.fieldRenamer != null) + { + this.fieldRenamer.visitProgramField(classBuilder.getProgramClass(), copiedField); + } + this.lastCopiedField = copiedField; + this.hasCopiedField = true; + } + + @Override + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) { + fieldrefConstant.referencedFieldAccept(this); + } + + public ProgramField getLastCopiedField() + { + return this.lastCopiedField; + } + + public boolean hasCopiedField() + { + return this.hasCopiedField; + } + + public void reset() + { + this.hasCopiedField = false; + } +} diff --git a/src/main/java/proguard/classfile/visitor/FieldRenamer.java b/src/main/java/proguard/classfile/visitor/FieldRenamer.java new file mode 100644 index 000000000..415ed1823 --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/FieldRenamer.java @@ -0,0 +1,46 @@ +package proguard.classfile.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.ProgramClass; +import proguard.classfile.ProgramField; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.Utf8Constant; +import proguard.classfile.constant.visitor.ConstantVisitor; + +public class FieldRenamer implements MemberVisitor, ConstantVisitor { + + private final String newFieldNamePrefix; + private int newFieldNameIndex; + + public FieldRenamer(String newFieldNamePrefix) + { + this.newFieldNamePrefix = newFieldNamePrefix; + this.newFieldNameIndex = 0; + } + + public void resetIndex() + { + this.newFieldNameIndex = 0; + } + + @Override + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + programClass.constantPoolEntryAccept(programField.u2nameIndex, this); + } + + @Override + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + @Override + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + this.newFieldNameIndex++; + utf8Constant.setString(this.newFieldNamePrefix + this.newFieldNameIndex); + } + + public String getNextFieldName() + { + return this.newFieldNamePrefix + (this.newFieldNameIndex + 1); + } +} From a33e5daa8dc2c9770eda87c311e805215461d674 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Tue, 22 Mar 2022 22:49:16 +0100 Subject: [PATCH 15/32] Use a FieldCopier in the MethodCopier --- .../classfile/visitor/MethodCopier.java | 87 +++++++++++++++++-- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/src/main/java/proguard/classfile/visitor/MethodCopier.java b/src/main/java/proguard/classfile/visitor/MethodCopier.java index e669705eb..550abfecf 100644 --- a/src/main/java/proguard/classfile/visitor/MethodCopier.java +++ b/src/main/java/proguard/classfile/visitor/MethodCopier.java @@ -6,30 +6,53 @@ import proguard.classfile.attribute.Attribute; import proguard.classfile.attribute.CodeAttribute; import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.FieldrefConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.editor.*; import proguard.classfile.instruction.ConstantInstruction; import proguard.classfile.instruction.Instruction; import proguard.classfile.instruction.visitor.InstructionVisitor; +import java.util.Objects; + public class MethodCopier implements MemberVisitor, AttributeVisitor, InstructionVisitor { private final ProgramClass destinationClass; private final ClassBuilder classBuilder; private final String newMethodNamePrefix; + private final String newMethodDescriptor; + private final int accessFlags; private final ConstantAdder constantAdder; private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); private final ExceptionInfoAdder exceptionInfoAdder; private int methodCounter = 0; + private final FieldRenamer fieldRenamer; + private final FieldCopier fieldCopier; private static final Logger logger = LogManager.getLogger(MethodCopier.class); + public MethodCopier(ProgramClass destinationClass, String newMethodNamePrefix, int accessFlags) + { + this(destinationClass, newMethodNamePrefix, null, accessFlags); + } - public MethodCopier(ProgramClass destinationClass, String newMethodNamePrefix) + public MethodCopier(ProgramClass destinationClass, String newMethodNamePrefix, String newMethodDescriptor, int accessFlags) { - this.destinationClass = destinationClass; - this.classBuilder = new ClassBuilder(destinationClass); + this(destinationClass, newMethodNamePrefix, newMethodDescriptor, accessFlags, null); + } + + public MethodCopier(ProgramClass destinationClass, String newMethodNamePrefix, String newMethodDescriptor, int accessFlags, FieldRenamer fieldRenamer) + { + this.destinationClass = destinationClass; + this.classBuilder = new ClassBuilder(destinationClass); this.newMethodNamePrefix = newMethodNamePrefix; - this.constantAdder = new ConstantAdder(destinationClass); - this.exceptionInfoAdder = new ExceptionInfoAdder(this.destinationClass, this.codeAttributeComposer); + this.newMethodDescriptor = newMethodDescriptor; + this.accessFlags = accessFlags; + this.fieldRenamer = fieldRenamer; + this.fieldCopier = new FieldCopier(this.classBuilder, this.fieldRenamer); + this.constantAdder = new ConstantAdder(destinationClass); + this.exceptionInfoAdder = new ExceptionInfoAdder(this.destinationClass, this.codeAttributeComposer); } private int getNewMethodIndex() @@ -46,14 +69,24 @@ public void visitAnyMember(Clazz clazz, Member member) { @Override public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { codeAttributeComposer.reset(); + if (this.fieldRenamer != null) + { + this.fieldRenamer.resetIndex(); + } programMethod.attributesAccept(programClass, this); int methodIndex = getNewMethodIndex(); String newMethodName = newMethodNamePrefix; if (methodIndex > 1) { + logger.warn(methodIndex + " methods were visited by MethodCopier(" + destinationClass + ", " + newMethodNamePrefix +")."); newMethodName += "$" + methodIndex; } - ProgramMethod newMethod = classBuilder.addAndReturnMethod(AccessConstants.PRIVATE, newMethodName, programMethod.getDescriptor(programClass)); + String methodDescriptor = programMethod.getDescriptor(programClass); + if (this.newMethodDescriptor != null) + { + methodDescriptor = this.newMethodDescriptor; + } + ProgramMethod newMethod = classBuilder.addAndReturnMethod(accessFlags, newMethodName, methodDescriptor); codeAttributeComposer.addCodeAttribute(this.destinationClass, newMethod); } @@ -79,8 +112,46 @@ public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAt @Override public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) { - // ensure the referenced constant is in the constant pool at the correct index - constantInstruction.constantIndex = this.constantAdder.addConstant(clazz, constantInstruction.constantIndex); + // TODO: Replace references to the lambda class itself by references to the new lambda group. + // (WIP) + this.fieldCopier.reset(); + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, new ConstantVisitor() { + + @Override + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + @Override + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + // TODO: replace lambda reference by lambda group reference + // Note: is it sufficient to only replace the class constant? + // or should the name of the class also be updated + if (Objects.equals(fieldrefConstant.referencedClass, clazz)) + { + // copy the field to the lambda group + fieldrefConstant.referencedFieldAccept(fieldCopier); + } + } + + @Override + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + if (Objects.equals(classConstant.referencedClass, clazz)) + { + logger.info("Class " + clazz + " references itself in a constant instruction: " + constantInstruction); + } + } + }); + if (this.fieldCopier.hasCopiedField()) + { + // add the necessary constants to the lambda group + constantInstruction.constantIndex = classBuilder.getConstantPoolEditor().addFieldrefConstant(destinationClass, fieldCopier.getLastCopiedField()); + } + else + { + // ensure the referenced constant is in the constant pool at the correct index + constantInstruction.constantIndex = this.constantAdder.addConstant(clazz, constantInstruction.constantIndex); + } // copy instruction codeAttributeComposer.appendInstruction(offset, constantInstruction); } From bf8155b1ae3992d97a8d0015088a8b7b1e7546b4 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Sun, 27 Mar 2022 22:24:50 +0200 Subject: [PATCH 16/32] FieldRenamer: use descriptor + index as new name --- .../classfile/visitor/FieldRenamer.java | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/main/java/proguard/classfile/visitor/FieldRenamer.java b/src/main/java/proguard/classfile/visitor/FieldRenamer.java index 415ed1823..b041862b3 100644 --- a/src/main/java/proguard/classfile/visitor/FieldRenamer.java +++ b/src/main/java/proguard/classfile/visitor/FieldRenamer.java @@ -1,21 +1,35 @@ package proguard.classfile.visitor; import proguard.classfile.Clazz; +import proguard.classfile.Field; import proguard.classfile.ProgramClass; import proguard.classfile.ProgramField; import proguard.classfile.constant.Constant; import proguard.classfile.constant.Utf8Constant; import proguard.classfile.constant.visitor.ConstantVisitor; +import java.util.HashMap; +import java.util.Map; + public class FieldRenamer implements MemberVisitor, ConstantVisitor { private final String newFieldNamePrefix; - private int newFieldNameIndex; + private int newFieldNameIndex = 0; + private final boolean useDescriptorBasedNames; + private final Map descriptorIndex = new HashMap<>(); + private Field lastVisitedField; + private Clazz lastVisitedClass; public FieldRenamer(String newFieldNamePrefix) { - this.newFieldNamePrefix = newFieldNamePrefix; - this.newFieldNameIndex = 0; + this.newFieldNamePrefix = newFieldNamePrefix; + this.useDescriptorBasedNames = false; + } + + public FieldRenamer(boolean useDescriptorBasedNames) + { + this.newFieldNamePrefix = ""; + this.useDescriptorBasedNames = useDescriptorBasedNames; } public void resetIndex() @@ -26,6 +40,8 @@ public void resetIndex() @Override public void visitProgramField(ProgramClass programClass, ProgramField programField) { + this.lastVisitedClass = programClass; + this.lastVisitedField = programField; programClass.constantPoolEntryAccept(programField.u2nameIndex, this); } @@ -35,12 +51,30 @@ public void visitAnyConstant(Clazz clazz, Constant constant) {} @Override public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) { + String newName = getNextFieldName(); + utf8Constant.setString(newName); this.newFieldNameIndex++; - utf8Constant.setString(this.newFieldNamePrefix + this.newFieldNameIndex); + String descriptor = this.lastVisitedField.getDescriptor(this.lastVisitedClass); + this.descriptorIndex.put(descriptor, this.descriptorIndex.getOrDefault(descriptor, 0) + 1); + } public String getNextFieldName() { - return this.newFieldNamePrefix + (this.newFieldNameIndex + 1); + String newName; + if (useDescriptorBasedNames) + { + // This is non-logical behaviour: the method name suggests a globally correct next name would be + // returned, but here it depends on the previously visited field, while in practice + // we don't know whether the next field will have the same descriptor + String descriptor = this.lastVisitedField.getDescriptor(this.lastVisitedClass); + newName = descriptor.replace(";", "").replace("/", "").replace("[", "") + this.descriptorIndex.getOrDefault(descriptor, 0); + + } + else + { + newName = this.newFieldNamePrefix + (this.newFieldNameIndex + 1); + } + return newName; } } From b2877ea5737da0c8a6268acc57d904b4dbaaa586 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Sun, 27 Mar 2022 22:28:35 +0200 Subject: [PATCH 17/32] Don't print warning in FieldCopier --- src/main/java/proguard/classfile/visitor/FieldCopier.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/proguard/classfile/visitor/FieldCopier.java b/src/main/java/proguard/classfile/visitor/FieldCopier.java index a1dd5d49c..5d63627a1 100644 --- a/src/main/java/proguard/classfile/visitor/FieldCopier.java +++ b/src/main/java/proguard/classfile/visitor/FieldCopier.java @@ -8,8 +8,6 @@ import proguard.classfile.editor.ClassBuilder; import proguard.classfile.editor.ClassEditor; -import java.util.Objects; - public class FieldCopier implements MemberVisitor, ConstantVisitor { private final ClassBuilder classBuilder; @@ -36,12 +34,12 @@ public void visitProgramField(ProgramClass programClass, ProgramField programFie } String fieldDescriptor = programField.getDescriptor(programClass); - Field oldField =classBuilder.getProgramClass().findField(fieldName, null); + Field oldField = classBuilder.getProgramClass().findField(fieldName, null); Field oldFieldSameDescriptor = classBuilder.getProgramClass().findField(fieldName, fieldDescriptor); if (oldField != null && oldFieldSameDescriptor == null) { String oldFieldDescriptor = oldField.getDescriptor(classBuilder.getProgramClass()); - logger.warn("Field " + fieldName + " already exists in class " + classBuilder.getProgramClass() + " with different descriptor: " + oldFieldDescriptor + " <-> " + fieldDescriptor + ". The field will be duplicated with different descriptors."); + //logger.warn("Field " + fieldName + " already exists in class " + classBuilder.getProgramClass() + " with different descriptor: " + oldFieldDescriptor + " <-> " + fieldDescriptor + ". The field will be duplicated with different descriptors."); // Merge the field types: generalise to a common super type //fieldDescriptor = ClassConstants.TYPE_JAVA_LANG_OBJECT; } From 8c8279695892bbcffc736e0bc0ecdde73960d887 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Sat, 7 May 2022 15:36:09 +0200 Subject: [PATCH 18/32] ClassConstantToClassVisitor: don't visit referenced class if it is null --- .../attribute/visitor/ClassConstantToClassVisitor.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java b/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java index 7707434b7..04ab18dc0 100644 --- a/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java +++ b/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java @@ -2,6 +2,7 @@ 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; @@ -14,9 +15,11 @@ 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) + if (this.classVisitor != null && classConstant.referencedClass != null) { classConstant.referencedClass.accept(this.classVisitor); } From 758be938a0ce122a747c0381e85c866249c9f746 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Sat, 7 May 2022 15:36:27 +0200 Subject: [PATCH 19/32] PackageGrouper: pretty print package names in log --- src/main/java/proguard/classfile/visitor/PackageGrouper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/proguard/classfile/visitor/PackageGrouper.java b/src/main/java/proguard/classfile/visitor/PackageGrouper.java index c0261dd27..b3ab94fca 100644 --- a/src/main/java/proguard/classfile/visitor/PackageGrouper.java +++ b/src/main/java/proguard/classfile/visitor/PackageGrouper.java @@ -25,7 +25,8 @@ public void visitAnyClass(Clazz clazz) // String classPackageName = ClassUtil.internalPackageName(clazz.getName()); if (!packageClassPools.containsKey(classPackageName)) { - logger.info("New package found: {}", classPackageName); + logger.info("New package found: {}", + ClassUtil.externalPackageName(ClassUtil.externalClassName(clazz.getName()))); packageClassPools.put(classPackageName, new ClassPool()); } packageClassPools.get(classPackageName).addClass(clazz); From 7821839e2b72f051710ea87143bb4347e0cd64d0 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Mon, 6 Jun 2022 18:34:34 +0200 Subject: [PATCH 20/32] Test added for MethodCopier --- .../classfile/visitor/MethodCopierTest.kt | 96 +++++++++++++++++++ src/test/kotlin/testutils/MatchDetector.kt | 28 ++++++ 2 files changed, 124 insertions(+) create mode 100644 src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt create mode 100644 src/test/kotlin/testutils/MatchDetector.kt diff --git a/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt b/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt new file mode 100644 index 000000000..24c32de67 --- /dev/null +++ b/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt @@ -0,0 +1,96 @@ +package proguard.classfile.visitor + +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import proguard.classfile.AccessConstants +import proguard.classfile.ClassConstants +import proguard.classfile.VersionConstants +import proguard.classfile.attribute.visitor.AllAttributeVisitor +import proguard.classfile.editor.ClassBuilder +import proguard.classfile.editor.InstructionSequenceBuilder +import proguard.classfile.instruction.visitor.AllInstructionVisitor +import proguard.classfile.util.InstructionSequenceMatcher +import testutils.MatchDetector + +class MethodCopierTest : FreeSpec({ + "Given a method and a target class" - { + val classBuilder = ClassBuilder( + VersionConstants.CLASS_VERSION_1_8, + AccessConstants.FINAL or AccessConstants.SUPER, + "Test", + ClassConstants.NAME_JAVA_LANG_OBJECT + ) + val testClass = classBuilder.programClass + val method = classBuilder.addAndReturnMethod(AccessConstants.PUBLIC, + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT, + 50) { + it + .aload_0() + .iconst(0) + .invokespecial( + ClassConstants.NAME_JAVA_LANG_OBJECT, + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT) + .return_() + } + val methodDescriptor = method.getDescriptor(testClass) + val methodName = method.getName(testClass) + val methodAccessFlags = AccessConstants.PUBLIC + val targetClass = ClassBuilder( + VersionConstants.CLASS_VERSION_1_8, + AccessConstants.PUBLIC, + "TargetClass", + ClassConstants.NAME_JAVA_LANG_OBJECT).programClass + "When the method is copied to the target class" - { + val methodCopier = MethodCopier(targetClass, ClassConstants.METHOD_NAME_INIT, methodAccessFlags) + method.accept(testClass, methodCopier) + "Then the target class contains a method with the correct name and descriptor" { + targetClass.findMethod(methodName, methodDescriptor) shouldNotBe null + } + + "Then the copied method has the correct access modifiers" { + val copiedMethod = targetClass.findMethod(methodName, methodDescriptor) + copiedMethod.accessFlags shouldBe methodAccessFlags + } + + "Then the instructions of the copied method match those of the original method" { + val builder = InstructionSequenceBuilder() + builder + .aload_0() + .iconst(0) + .invokespecial( + ClassConstants.NAME_JAVA_LANG_OBJECT, + ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT) + .return_() + val matchDetector = MatchDetector(InstructionSequenceMatcher(builder.constants(), builder.instructions())) + val copiedMethod = targetClass.findMethod(methodName, methodDescriptor) + copiedMethod.accept(targetClass, + AllAttributeVisitor( + AllInstructionVisitor( + matchDetector))) + matchDetector.matchIsFound shouldBe true + } + } + + "When the method is copied to the target class with a new descriptor" - { + val newDescriptor = "(I)V" + val methodCopier = MethodCopier(targetClass, methodName, newDescriptor, methodAccessFlags) + method.accept(testClass, methodCopier) + "Then the target class contains a method with the correct name and descriptor" { + targetClass.findMethod(methodName, newDescriptor) shouldNotBe null + } + } + + "When the method is copied to the target class with a new name prefix" - { + val newNamePrefix = "copiedMethod" + val methodCopier = MethodCopier(targetClass, newNamePrefix, methodAccessFlags) + method.accept(testClass, methodCopier) + "Then the target class contains a method with the correct name and descriptor" { + targetClass.findMethod(newNamePrefix, methodDescriptor) shouldNotBe null + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/testutils/MatchDetector.kt b/src/test/kotlin/testutils/MatchDetector.kt new file mode 100644 index 000000000..dcedc6721 --- /dev/null +++ b/src/test/kotlin/testutils/MatchDetector.kt @@ -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) + } + } +} \ No newline at end of file From b542b9bb675a55d97382cd71bfc2b488cf626e02 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Mon, 6 Jun 2022 20:35:18 +0200 Subject: [PATCH 21/32] Apply ktlint formatting to new test classes --- .../classfile/visitor/MethodCopierTest.kt | 43 +++++++++++-------- src/test/kotlin/testutils/MatchDetector.kt | 2 +- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt b/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt index 24c32de67..e96e436e5 100644 --- a/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt +++ b/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt @@ -16,33 +16,37 @@ import testutils.MatchDetector class MethodCopierTest : FreeSpec({ "Given a method and a target class" - { val classBuilder = ClassBuilder( - VersionConstants.CLASS_VERSION_1_8, - AccessConstants.FINAL or AccessConstants.SUPER, - "Test", - ClassConstants.NAME_JAVA_LANG_OBJECT - ) + VersionConstants.CLASS_VERSION_1_8, + AccessConstants.FINAL or AccessConstants.SUPER, + "Test", + ClassConstants.NAME_JAVA_LANG_OBJECT + ) val testClass = classBuilder.programClass - val method = classBuilder.addAndReturnMethod(AccessConstants.PUBLIC, + val method = classBuilder.addAndReturnMethod( + AccessConstants.PUBLIC, ClassConstants.METHOD_NAME_INIT, ClassConstants.METHOD_TYPE_INIT, - 50) { + 50 + ) { it .aload_0() .iconst(0) .invokespecial( ClassConstants.NAME_JAVA_LANG_OBJECT, ClassConstants.METHOD_NAME_INIT, - ClassConstants.METHOD_TYPE_INIT) + ClassConstants.METHOD_TYPE_INIT + ) .return_() } - val methodDescriptor = method.getDescriptor(testClass) - val methodName = method.getName(testClass) + val methodDescriptor = method.getDescriptor(testClass) + val methodName = method.getName(testClass) val methodAccessFlags = AccessConstants.PUBLIC val targetClass = ClassBuilder( VersionConstants.CLASS_VERSION_1_8, AccessConstants.PUBLIC, "TargetClass", - ClassConstants.NAME_JAVA_LANG_OBJECT).programClass + ClassConstants.NAME_JAVA_LANG_OBJECT + ).programClass "When the method is copied to the target class" - { val methodCopier = MethodCopier(targetClass, ClassConstants.METHOD_NAME_INIT, methodAccessFlags) method.accept(testClass, methodCopier) @@ -63,14 +67,19 @@ class MethodCopierTest : FreeSpec({ .invokespecial( ClassConstants.NAME_JAVA_LANG_OBJECT, ClassConstants.METHOD_NAME_INIT, - ClassConstants.METHOD_TYPE_INIT) + ClassConstants.METHOD_TYPE_INIT + ) .return_() val matchDetector = MatchDetector(InstructionSequenceMatcher(builder.constants(), builder.instructions())) val copiedMethod = targetClass.findMethod(methodName, methodDescriptor) - copiedMethod.accept(targetClass, - AllAttributeVisitor( - AllInstructionVisitor( - matchDetector))) + copiedMethod.accept( + targetClass, + AllAttributeVisitor( + AllInstructionVisitor( + matchDetector + ) + ) + ) matchDetector.matchIsFound shouldBe true } } @@ -93,4 +102,4 @@ class MethodCopierTest : FreeSpec({ } } } -}) \ No newline at end of file +}) diff --git a/src/test/kotlin/testutils/MatchDetector.kt b/src/test/kotlin/testutils/MatchDetector.kt index dcedc6721..0e722f8be 100644 --- a/src/test/kotlin/testutils/MatchDetector.kt +++ b/src/test/kotlin/testutils/MatchDetector.kt @@ -25,4 +25,4 @@ class MatchDetector(val matcher: InstructionSequenceMatcher, vararg val argument matchedArguments = matcher.matchedArguments(arguments) } } -} \ No newline at end of file +} From 4ef0e592452ae037fa7f9e1f839b801abd803f10 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 8 Jun 2022 12:11:24 +0200 Subject: [PATCH 22/32] Inner classes of lambda merging moved to ProGuard CORE --- .../ClassConstantReferenceUpdater.java | 28 ++++++++++ .../visitor/ClassReferenceFinder.java | 32 +++++++++++ .../classfile/visitor/CodeSizeCounter.java | 29 ++++++++++ .../visitor/DescriptorTypeUpdater.java | 25 +++++++++ .../classfile/visitor/InnerClassRemover.java | 54 +++++++++++++++++++ .../visitor/MethodReferenceFinder.java | 34 ++++++++++++ 6 files changed, 202 insertions(+) create mode 100644 src/main/java/proguard/classfile/visitor/ClassConstantReferenceUpdater.java create mode 100644 src/main/java/proguard/classfile/visitor/ClassReferenceFinder.java create mode 100644 src/main/java/proguard/classfile/visitor/CodeSizeCounter.java create mode 100644 src/main/java/proguard/classfile/visitor/DescriptorTypeUpdater.java create mode 100644 src/main/java/proguard/classfile/visitor/InnerClassRemover.java create mode 100644 src/main/java/proguard/classfile/visitor/MethodReferenceFinder.java diff --git a/src/main/java/proguard/classfile/visitor/ClassConstantReferenceUpdater.java b/src/main/java/proguard/classfile/visitor/ClassConstantReferenceUpdater.java new file mode 100644 index 000000000..e50fad1b6 --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/ClassConstantReferenceUpdater.java @@ -0,0 +1,28 @@ +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 ClassConstantReferenceUpdater implements ConstantVisitor +{ + private final Clazz originalClass; + private final Clazz replacingClass; + public ClassConstantReferenceUpdater(Clazz originalClass, Clazz replacingClass) + { + this.originalClass = originalClass; + this.replacingClass = replacingClass; + } + + @Override + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + @Override + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { + if (classConstant.referencedClass == originalClass) + { + classConstant.referencedClass = replacingClass; + } + } +} diff --git a/src/main/java/proguard/classfile/visitor/ClassReferenceFinder.java b/src/main/java/proguard/classfile/visitor/ClassReferenceFinder.java new file mode 100644 index 000000000..355c65893 --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/ClassReferenceFinder.java @@ -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 +{ + 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; + } +} diff --git a/src/main/java/proguard/classfile/visitor/CodeSizeCounter.java b/src/main/java/proguard/classfile/visitor/CodeSizeCounter.java new file mode 100644 index 000000000..61b821b34 --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/CodeSizeCounter.java @@ -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; + } +} diff --git a/src/main/java/proguard/classfile/visitor/DescriptorTypeUpdater.java b/src/main/java/proguard/classfile/visitor/DescriptorTypeUpdater.java new file mode 100644 index 000000000..aa6cf4ea3 --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/DescriptorTypeUpdater.java @@ -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)); + } +} diff --git a/src/main/java/proguard/classfile/visitor/InnerClassRemover.java b/src/main/java/proguard/classfile/visitor/InnerClassRemover.java new file mode 100644 index 000000000..655e6a54d --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/InnerClassRemover.java @@ -0,0 +1,54 @@ +package proguard.classfile.visitor; + +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 proguard.classfile.editor.InnerClassesAttributeEditor; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class InnerClassRemover implements AttributeVisitor, InnerClassesInfoVisitor +{ + private final Clazz classToBeRemoved; + private final Set 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); + } + } +} diff --git a/src/main/java/proguard/classfile/visitor/MethodReferenceFinder.java b/src/main/java/proguard/classfile/visitor/MethodReferenceFinder.java new file mode 100644 index 000000000..2f9c82fb6 --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/MethodReferenceFinder.java @@ -0,0 +1,34 @@ +package proguard.classfile.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.Method; +import proguard.classfile.constant.AnyMethodrefConstant; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.visitor.ConstantVisitor; + +public class MethodReferenceFinder implements ConstantVisitor +{ + private final Method referencedMethod; + private boolean methodIsReferenced = false; + + public MethodReferenceFinder(Method referencedMethod) + { + this.referencedMethod = referencedMethod; + } + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + public void visitAnyMethodrefConstant(Clazz clazz, AnyMethodrefConstant anyMethodrefConstant) + { + if (anyMethodrefConstant.referencedMethod != null + && anyMethodrefConstant.referencedMethod.equals(referencedMethod)) + { + this.methodIsReferenced = true; + } + } + + public boolean methodReferenceFound() + { + return this.methodIsReferenced; + } +} From 67cde7f116cb2428e5afe46ab84341c54a496536 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 8 Jun 2022 13:30:04 +0200 Subject: [PATCH 23/32] New ConstantVisitor: FieldReferenceFinder --- .../visitor/FieldReferenceFinder.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/java/proguard/classfile/visitor/FieldReferenceFinder.java diff --git a/src/main/java/proguard/classfile/visitor/FieldReferenceFinder.java b/src/main/java/proguard/classfile/visitor/FieldReferenceFinder.java new file mode 100644 index 000000000..0ec8d309c --- /dev/null +++ b/src/main/java/proguard/classfile/visitor/FieldReferenceFinder.java @@ -0,0 +1,46 @@ +package proguard.classfile.visitor; + +import proguard.classfile.Clazz; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.FieldrefConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.ClassUtil; +import proguard.util.ListParser; +import proguard.util.NameParser; +import proguard.util.StringMatcher; + +public class FieldReferenceFinder implements ConstantVisitor { + + private final Clazz referencedClass; + private final StringMatcher regularExpressionMatcher; + private final ConstantVisitor constantVisitor; + private boolean fieldReferenceFound = false; + + public FieldReferenceFinder(Clazz referencedClass, + String fieldNameRegularExpression, + ConstantVisitor constantVisitor) + { + this.referencedClass = referencedClass; + this.regularExpressionMatcher = new ListParser(new NameParser(null)).parse(fieldNameRegularExpression); + this.constantVisitor = constantVisitor; + } + + @Override + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + @Override + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + if (fieldrefConstant.referencedClass == referencedClass + && this.regularExpressionMatcher.matches(fieldrefConstant.getName(clazz))) + { + this.fieldReferenceFound = true; + fieldrefConstant.accept(clazz, this.constantVisitor); + } + } + + public boolean isFieldReferenceFound() + { + return this.fieldReferenceFound; + } +} From 72a80b91c7c8e34a3b33d99b7c36b5acf6f1fdc0 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 29 Jun 2022 16:06:20 +0200 Subject: [PATCH 24/32] Move Kotlin specific constants from ClassConstants to KotlinConstants --- src/main/java/proguard/classfile/ClassConstants.java | 4 ---- src/main/java/proguard/classfile/kotlin/KotlinConstants.java | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/proguard/classfile/ClassConstants.java b/src/main/java/proguard/classfile/ClassConstants.java index 3aa8ceaf8..7a5131dfa 100644 --- a/src/main/java/proguard/classfile/ClassConstants.java +++ b/src/main/java/proguard/classfile/ClassConstants.java @@ -114,10 +114,6 @@ public class ClassConstants public static final String NAME_ANDROID_WEBKIT_WEB_VIEW = "android/webkit/WebView"; public static final String NAME_ANDROID_SUPPORT_V4_APP_FRAGMENT = "android/support/v4/app/Fragment"; - public static final String NAME_KOTLIN_UNIT = "kotlin/Unit"; - public static final String TYPE_KOTLIN_UNIT = "Lkotlin/Unit;"; - public static final String FIELD_NAME_KOTLIN_UNIT_INSTANCE = "INSTANCE"; - public static final String NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicIntegerFieldUpdater"; public static final String NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicLongFieldUpdater"; public static final String NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER = "java/util/concurrent/atomic/AtomicReferenceFieldUpdater"; diff --git a/src/main/java/proguard/classfile/kotlin/KotlinConstants.java b/src/main/java/proguard/classfile/kotlin/KotlinConstants.java index d02f55791..603f5408b 100644 --- a/src/main/java/proguard/classfile/kotlin/KotlinConstants.java +++ b/src/main/java/proguard/classfile/kotlin/KotlinConstants.java @@ -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"; From 7eeff483ccdef2342a14ca5dbbe68add58176245 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 29 Jun 2022 16:15:50 +0200 Subject: [PATCH 25/32] Add PGC header to ClassConstantToClassVisitor --- .../visitor/ClassConstantToClassVisitor.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java b/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java index 04ab18dc0..651b02733 100644 --- a/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java +++ b/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java @@ -1,3 +1,20 @@ +/* + * 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; import proguard.classfile.Clazz; @@ -5,8 +22,16 @@ import proguard.classfile.constant.Constant; import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.visitor.ClassVisitor; +import proguard.classfile.visitor.MemberVisitor; -public class ClassConstantToClassVisitor implements ConstantVisitor { +/** + * 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; From 02e8fd04b3669df810eb760d15c2e3e4ed131497 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 29 Jun 2022 16:16:23 +0200 Subject: [PATCH 26/32] Open '{' on new line in ClassConstantToClassVisitor --- .../attribute/visitor/ClassConstantToClassVisitor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java b/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java index 651b02733..a232e4880 100644 --- a/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java +++ b/src/main/java/proguard/classfile/attribute/visitor/ClassConstantToClassVisitor.java @@ -43,7 +43,8 @@ public ClassConstantToClassVisitor(ClassVisitor classVisitor) public void visitAnyConstant(Clazz clazz, Constant constant) {} @Override - public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { if (this.classVisitor != null && classConstant.referencedClass != null) { classConstant.referencedClass.accept(this.classVisitor); From 660a391b76e5fb0bc87b359cd486e816bc9d2b60 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 29 Jun 2022 16:18:10 +0200 Subject: [PATCH 27/32] Remove redundant ClassMethodFilter --- .../classfile/visitor/ClassMethodFilter.java | 52 ------------------- 1 file changed, 52 deletions(-) delete mode 100644 src/main/java/proguard/classfile/visitor/ClassMethodFilter.java diff --git a/src/main/java/proguard/classfile/visitor/ClassMethodFilter.java b/src/main/java/proguard/classfile/visitor/ClassMethodFilter.java deleted file mode 100644 index 7d3090ddd..000000000 --- a/src/main/java/proguard/classfile/visitor/ClassMethodFilter.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * ProGuard -- shrinking, optimization, obfuscation, and preverification - * of Java bytecode. - * - * Copyright (c) 2002-2021 Guardsquare NV - */ - -package proguard.classfile.visitor; - -import proguard.classfile.*; - -/** - * This {@link ClassVisitor} delegates its visits to given - * acceptVisitor when the visited class has method with - * given name and descriptor and delegates to rejectVisitor when - * none of the class methods match. - */ -public class ClassMethodFilter -implements ClassVisitor -{ - private final String methodName; - private final String descriptor; - private final ClassVisitor acceptVisitor; - private final ClassVisitor rejectVisitor; - - public ClassMethodFilter(String methodName, - String descriptor, - ClassVisitor acceptVisitor, - ClassVisitor rejectVisitor) - { - this.methodName = methodName; - this.descriptor = descriptor; - this.acceptVisitor = acceptVisitor; - this.rejectVisitor = rejectVisitor; - } - - @Override - public void visitAnyClass(Clazz clazz) - { - ClassVisitor delegateVisitor = delegateVisitor(clazz); - if (delegateVisitor != null) - { - delegateVisitor.visitAnyClass(clazz); - } - } - - private ClassVisitor delegateVisitor(Clazz clazz) - { - Method method = clazz.findMethod(methodName, descriptor); - return method != null ? acceptVisitor : rejectVisitor; - } -} \ No newline at end of file From 59f79992569e88f2366f75468d4baab3b9c577bf Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 29 Jun 2022 16:28:33 +0200 Subject: [PATCH 28/32] Move visitors to ProGuard Classes involved are: InnerClasssInfoClassConstantVisitor, ClassConstantReferenceUpdater, FieldReferenceFinder, FieldRenamer, MethodCopier, MethodReferenceFinder and PackageGrouper --- .../InnerClassInfoClassConstantVisitor.java | 30 ---- .../ClassConstantReferenceUpdater.java | 28 ---- .../visitor/FieldReferenceFinder.java | 46 ----- .../classfile/visitor/FieldRenamer.java | 80 --------- .../classfile/visitor/MethodCopier.java | 158 ------------------ .../visitor/MethodReferenceFinder.java | 34 ---- .../classfile/visitor/PackageGrouper.java | 66 -------- .../classfile/visitor/PackageGrouperTest.kt | 98 ----------- 8 files changed, 540 deletions(-) delete mode 100644 src/main/java/proguard/classfile/attribute/visitor/InnerClassInfoClassConstantVisitor.java delete mode 100644 src/main/java/proguard/classfile/visitor/ClassConstantReferenceUpdater.java delete mode 100644 src/main/java/proguard/classfile/visitor/FieldReferenceFinder.java delete mode 100644 src/main/java/proguard/classfile/visitor/FieldRenamer.java delete mode 100644 src/main/java/proguard/classfile/visitor/MethodCopier.java delete mode 100644 src/main/java/proguard/classfile/visitor/MethodReferenceFinder.java delete mode 100644 src/main/java/proguard/classfile/visitor/PackageGrouper.java delete mode 100644 src/test/kotlin/proguard/classfile/visitor/PackageGrouperTest.kt diff --git a/src/main/java/proguard/classfile/attribute/visitor/InnerClassInfoClassConstantVisitor.java b/src/main/java/proguard/classfile/attribute/visitor/InnerClassInfoClassConstantVisitor.java deleted file mode 100644 index b3020b62d..000000000 --- a/src/main/java/proguard/classfile/attribute/visitor/InnerClassInfoClassConstantVisitor.java +++ /dev/null @@ -1,30 +0,0 @@ -package proguard.classfile.attribute.visitor; - -import proguard.classfile.Clazz; -import proguard.classfile.ProgramClass; -import proguard.classfile.attribute.InnerClassesInfo; -import proguard.classfile.constant.visitor.ConstantVisitor; - -public class InnerClassInfoClassConstantVisitor implements InnerClassesInfoVisitor { - - private final ConstantVisitor innerClassConstantVisitor; - private final ConstantVisitor outerClassConstantVisitor; - - public InnerClassInfoClassConstantVisitor(ConstantVisitor innerClassConstantVisitor, ConstantVisitor outerClassConstantVisitor) - { - this.innerClassConstantVisitor = innerClassConstantVisitor; - this.outerClassConstantVisitor = outerClassConstantVisitor; - } - - @Override - public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) { - if (this.innerClassConstantVisitor != null) - { - innerClassesInfo.innerClassConstantAccept(clazz, this.innerClassConstantVisitor); - } - if (this.outerClassConstantVisitor != null) - { - innerClassesInfo.outerClassConstantAccept(clazz, this.outerClassConstantVisitor); - } - } -} diff --git a/src/main/java/proguard/classfile/visitor/ClassConstantReferenceUpdater.java b/src/main/java/proguard/classfile/visitor/ClassConstantReferenceUpdater.java deleted file mode 100644 index e50fad1b6..000000000 --- a/src/main/java/proguard/classfile/visitor/ClassConstantReferenceUpdater.java +++ /dev/null @@ -1,28 +0,0 @@ -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 ClassConstantReferenceUpdater implements ConstantVisitor -{ - private final Clazz originalClass; - private final Clazz replacingClass; - public ClassConstantReferenceUpdater(Clazz originalClass, Clazz replacingClass) - { - this.originalClass = originalClass; - this.replacingClass = replacingClass; - } - - @Override - public void visitAnyConstant(Clazz clazz, Constant constant) {} - - @Override - public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { - if (classConstant.referencedClass == originalClass) - { - classConstant.referencedClass = replacingClass; - } - } -} diff --git a/src/main/java/proguard/classfile/visitor/FieldReferenceFinder.java b/src/main/java/proguard/classfile/visitor/FieldReferenceFinder.java deleted file mode 100644 index 0ec8d309c..000000000 --- a/src/main/java/proguard/classfile/visitor/FieldReferenceFinder.java +++ /dev/null @@ -1,46 +0,0 @@ -package proguard.classfile.visitor; - -import proguard.classfile.Clazz; -import proguard.classfile.constant.Constant; -import proguard.classfile.constant.FieldrefConstant; -import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.util.ClassUtil; -import proguard.util.ListParser; -import proguard.util.NameParser; -import proguard.util.StringMatcher; - -public class FieldReferenceFinder implements ConstantVisitor { - - private final Clazz referencedClass; - private final StringMatcher regularExpressionMatcher; - private final ConstantVisitor constantVisitor; - private boolean fieldReferenceFound = false; - - public FieldReferenceFinder(Clazz referencedClass, - String fieldNameRegularExpression, - ConstantVisitor constantVisitor) - { - this.referencedClass = referencedClass; - this.regularExpressionMatcher = new ListParser(new NameParser(null)).parse(fieldNameRegularExpression); - this.constantVisitor = constantVisitor; - } - - @Override - public void visitAnyConstant(Clazz clazz, Constant constant) {} - - @Override - public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) - { - if (fieldrefConstant.referencedClass == referencedClass - && this.regularExpressionMatcher.matches(fieldrefConstant.getName(clazz))) - { - this.fieldReferenceFound = true; - fieldrefConstant.accept(clazz, this.constantVisitor); - } - } - - public boolean isFieldReferenceFound() - { - return this.fieldReferenceFound; - } -} diff --git a/src/main/java/proguard/classfile/visitor/FieldRenamer.java b/src/main/java/proguard/classfile/visitor/FieldRenamer.java deleted file mode 100644 index b041862b3..000000000 --- a/src/main/java/proguard/classfile/visitor/FieldRenamer.java +++ /dev/null @@ -1,80 +0,0 @@ -package proguard.classfile.visitor; - -import proguard.classfile.Clazz; -import proguard.classfile.Field; -import proguard.classfile.ProgramClass; -import proguard.classfile.ProgramField; -import proguard.classfile.constant.Constant; -import proguard.classfile.constant.Utf8Constant; -import proguard.classfile.constant.visitor.ConstantVisitor; - -import java.util.HashMap; -import java.util.Map; - -public class FieldRenamer implements MemberVisitor, ConstantVisitor { - - private final String newFieldNamePrefix; - private int newFieldNameIndex = 0; - private final boolean useDescriptorBasedNames; - private final Map descriptorIndex = new HashMap<>(); - private Field lastVisitedField; - private Clazz lastVisitedClass; - - public FieldRenamer(String newFieldNamePrefix) - { - this.newFieldNamePrefix = newFieldNamePrefix; - this.useDescriptorBasedNames = false; - } - - public FieldRenamer(boolean useDescriptorBasedNames) - { - this.newFieldNamePrefix = ""; - this.useDescriptorBasedNames = useDescriptorBasedNames; - } - - public void resetIndex() - { - this.newFieldNameIndex = 0; - } - - @Override - public void visitProgramField(ProgramClass programClass, ProgramField programField) - { - this.lastVisitedClass = programClass; - this.lastVisitedField = programField; - programClass.constantPoolEntryAccept(programField.u2nameIndex, this); - } - - @Override - public void visitAnyConstant(Clazz clazz, Constant constant) {} - - @Override - public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) - { - String newName = getNextFieldName(); - utf8Constant.setString(newName); - this.newFieldNameIndex++; - String descriptor = this.lastVisitedField.getDescriptor(this.lastVisitedClass); - this.descriptorIndex.put(descriptor, this.descriptorIndex.getOrDefault(descriptor, 0) + 1); - - } - - public String getNextFieldName() - { - String newName; - if (useDescriptorBasedNames) - { - // This is non-logical behaviour: the method name suggests a globally correct next name would be - // returned, but here it depends on the previously visited field, while in practice - // we don't know whether the next field will have the same descriptor - String descriptor = this.lastVisitedField.getDescriptor(this.lastVisitedClass); - newName = descriptor.replace(";", "").replace("/", "").replace("[", "") + this.descriptorIndex.getOrDefault(descriptor, 0); - - } - else - { - newName = this.newFieldNamePrefix + (this.newFieldNameIndex + 1); - } - return newName; - } -} diff --git a/src/main/java/proguard/classfile/visitor/MethodCopier.java b/src/main/java/proguard/classfile/visitor/MethodCopier.java deleted file mode 100644 index 550abfecf..000000000 --- a/src/main/java/proguard/classfile/visitor/MethodCopier.java +++ /dev/null @@ -1,158 +0,0 @@ -package proguard.classfile.visitor; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import proguard.classfile.*; -import proguard.classfile.attribute.Attribute; -import proguard.classfile.attribute.CodeAttribute; -import proguard.classfile.attribute.visitor.AttributeVisitor; -import proguard.classfile.constant.ClassConstant; -import proguard.classfile.constant.Constant; -import proguard.classfile.constant.FieldrefConstant; -import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.editor.*; -import proguard.classfile.instruction.ConstantInstruction; -import proguard.classfile.instruction.Instruction; -import proguard.classfile.instruction.visitor.InstructionVisitor; - -import java.util.Objects; - -public class MethodCopier implements MemberVisitor, AttributeVisitor, InstructionVisitor -{ - private final ProgramClass destinationClass; - private final ClassBuilder classBuilder; - private final String newMethodNamePrefix; - private final String newMethodDescriptor; - private final int accessFlags; - private final ConstantAdder constantAdder; - private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); - private final ExceptionInfoAdder exceptionInfoAdder; - private int methodCounter = 0; - private final FieldRenamer fieldRenamer; - private final FieldCopier fieldCopier; - private static final Logger logger = LogManager.getLogger(MethodCopier.class); - - public MethodCopier(ProgramClass destinationClass, String newMethodNamePrefix, int accessFlags) - { - this(destinationClass, newMethodNamePrefix, null, accessFlags); - } - - public MethodCopier(ProgramClass destinationClass, String newMethodNamePrefix, String newMethodDescriptor, int accessFlags) - { - this(destinationClass, newMethodNamePrefix, newMethodDescriptor, accessFlags, null); - } - - public MethodCopier(ProgramClass destinationClass, String newMethodNamePrefix, String newMethodDescriptor, int accessFlags, FieldRenamer fieldRenamer) - { - this.destinationClass = destinationClass; - this.classBuilder = new ClassBuilder(destinationClass); - this.newMethodNamePrefix = newMethodNamePrefix; - this.newMethodDescriptor = newMethodDescriptor; - this.accessFlags = accessFlags; - this.fieldRenamer = fieldRenamer; - this.fieldCopier = new FieldCopier(this.classBuilder, this.fieldRenamer); - this.constantAdder = new ConstantAdder(destinationClass); - this.exceptionInfoAdder = new ExceptionInfoAdder(this.destinationClass, this.codeAttributeComposer); - } - - private int getNewMethodIndex() - { - int methodIndex = this.methodCounter; - this.methodCounter++; - return methodIndex; - } - - @Override - public void visitAnyMember(Clazz clazz, Member member) { - } - - @Override - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { - codeAttributeComposer.reset(); - if (this.fieldRenamer != null) - { - this.fieldRenamer.resetIndex(); - } - programMethod.attributesAccept(programClass, this); - int methodIndex = getNewMethodIndex(); - String newMethodName = newMethodNamePrefix; - if (methodIndex > 1) - { - logger.warn(methodIndex + " methods were visited by MethodCopier(" + destinationClass + ", " + newMethodNamePrefix +")."); - newMethodName += "$" + methodIndex; - } - String methodDescriptor = programMethod.getDescriptor(programClass); - if (this.newMethodDescriptor != null) - { - methodDescriptor = this.newMethodDescriptor; - } - ProgramMethod newMethod = classBuilder.addAndReturnMethod(accessFlags, newMethodName, methodDescriptor); - codeAttributeComposer.addCodeAttribute(this.destinationClass, newMethod); - } - - @Override - public void visitAnyAttribute(Clazz clazz, Attribute attribute) { - } - - @Override - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { - codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength); - // copy code and exceptions - codeAttribute.instructionsAccept(clazz, method, this); - codeAttribute.exceptionsAccept(clazz, method, this.exceptionInfoAdder); - codeAttribute.attributesAccept(clazz, method, this); - codeAttributeComposer.endCodeFragment(); - } - - @Override - public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) { - // copy instruction - codeAttributeComposer.appendInstruction(offset, instruction); - } - - @Override - public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) { - // TODO: Replace references to the lambda class itself by references to the new lambda group. - // (WIP) - this.fieldCopier.reset(); - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, new ConstantVisitor() { - - @Override - public void visitAnyConstant(Clazz clazz, Constant constant) {} - - @Override - public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) - { - // TODO: replace lambda reference by lambda group reference - // Note: is it sufficient to only replace the class constant? - // or should the name of the class also be updated - if (Objects.equals(fieldrefConstant.referencedClass, clazz)) - { - // copy the field to the lambda group - fieldrefConstant.referencedFieldAccept(fieldCopier); - } - } - - @Override - public void visitClassConstant(Clazz clazz, ClassConstant classConstant) - { - if (Objects.equals(classConstant.referencedClass, clazz)) - { - logger.info("Class " + clazz + " references itself in a constant instruction: " + constantInstruction); - } - } - }); - if (this.fieldCopier.hasCopiedField()) - { - // add the necessary constants to the lambda group - constantInstruction.constantIndex = classBuilder.getConstantPoolEditor().addFieldrefConstant(destinationClass, fieldCopier.getLastCopiedField()); - } - else - { - // ensure the referenced constant is in the constant pool at the correct index - constantInstruction.constantIndex = this.constantAdder.addConstant(clazz, constantInstruction.constantIndex); - } - // copy instruction - codeAttributeComposer.appendInstruction(offset, constantInstruction); - } -} diff --git a/src/main/java/proguard/classfile/visitor/MethodReferenceFinder.java b/src/main/java/proguard/classfile/visitor/MethodReferenceFinder.java deleted file mode 100644 index 2f9c82fb6..000000000 --- a/src/main/java/proguard/classfile/visitor/MethodReferenceFinder.java +++ /dev/null @@ -1,34 +0,0 @@ -package proguard.classfile.visitor; - -import proguard.classfile.Clazz; -import proguard.classfile.Method; -import proguard.classfile.constant.AnyMethodrefConstant; -import proguard.classfile.constant.Constant; -import proguard.classfile.constant.visitor.ConstantVisitor; - -public class MethodReferenceFinder implements ConstantVisitor -{ - private final Method referencedMethod; - private boolean methodIsReferenced = false; - - public MethodReferenceFinder(Method referencedMethod) - { - this.referencedMethod = referencedMethod; - } - - public void visitAnyConstant(Clazz clazz, Constant constant) {} - - public void visitAnyMethodrefConstant(Clazz clazz, AnyMethodrefConstant anyMethodrefConstant) - { - if (anyMethodrefConstant.referencedMethod != null - && anyMethodrefConstant.referencedMethod.equals(referencedMethod)) - { - this.methodIsReferenced = true; - } - } - - public boolean methodReferenceFound() - { - return this.methodIsReferenced; - } -} diff --git a/src/main/java/proguard/classfile/visitor/PackageGrouper.java b/src/main/java/proguard/classfile/visitor/PackageGrouper.java deleted file mode 100644 index b3ab94fca..000000000 --- a/src/main/java/proguard/classfile/visitor/PackageGrouper.java +++ /dev/null @@ -1,66 +0,0 @@ -package proguard.classfile.visitor; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import proguard.classfile.ClassPool; -import proguard.classfile.Clazz; -import proguard.classfile.util.ClassUtil; - -import java.util.HashMap; -import java.util.Map; - -/** - * This class - */ -public class PackageGrouper implements ClassVisitor { - - private final Map packageClassPools = new HashMap<>(); - private static final Logger logger = LogManager.getLogger(PackageGrouper.class); - - @Override - public void visitAnyClass(Clazz clazz) - { - String classPackageName = ClassUtil.internalPackageName(clazz.getName()); - // or - // String classPackageName = ClassUtil.internalPackageName(clazz.getName()); - if (!packageClassPools.containsKey(classPackageName)) - { - logger.info("New package found: {}", - ClassUtil.externalPackageName(ClassUtil.externalClassName(clazz.getName()))); - packageClassPools.put(classPackageName, new ClassPool()); - } - packageClassPools.get(classPackageName).addClass(clazz); - } - - public int size() - { - return packageClassPools.size(); - } - - public boolean containsPackage(String packageName) - { - return packageClassPools.containsKey(packageName); - } - - public Iterable packageNames() - { - return packageClassPools.keySet(); - } - - public void packagesAccept(ClassPoolVisitor classPoolVisitor) - { - for (ClassPool packageClassPool : packageClassPools.values()) - { - classPoolVisitor.visitClassPool(packageClassPool); - } - } - - public void packageAccept(String packageName, ClassPoolVisitor classPoolVisitor) - { - ClassPool packageClassPool = this.packageClassPools.get(packageName); - if (packageClassPool != null) - { - classPoolVisitor.visitClassPool(packageClassPool); - } - } -} diff --git a/src/test/kotlin/proguard/classfile/visitor/PackageGrouperTest.kt b/src/test/kotlin/proguard/classfile/visitor/PackageGrouperTest.kt deleted file mode 100644 index 7b659b761..000000000 --- a/src/test/kotlin/proguard/classfile/visitor/PackageGrouperTest.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * ProGuard -- shrinking, optimization, obfuscation, and preverification - * of Java bytecode. - * - * Copyright (c) 2002-2021 Guardsquare NV - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -package proguard.classfile.visitor - -import io.kotest.core.spec.style.FreeSpec -import io.kotest.matchers.collections.shouldBeIn -import io.kotest.matchers.shouldBe -import proguard.classfile.ClassPool -import proguard.classfile.util.ClassUtil -import testutils.ClassPoolBuilder -import testutils.KotlinSource - -class PackageGrouperTest : FreeSpec({ - - val (programClassPool, _) = ClassPoolBuilder.fromSource( - KotlinSource( - "Test.kt", - """ - package app.package1 - fun main(index: Int) { - val lambda1 = { - println("Lambda1") - } - lambda1() - } - """.trimIndent() - ), - KotlinSource( - "Test2.kt", - """ - package app.package2 - fun main() { - val lambda2 = { println("Lambda2") } - lambda2() - } - """.trimIndent() - ) - ) - - "Given a PackageGrouper" - { - val grouper = PackageGrouper() - "When the grouper is applied to classes of different packages" - { - programClassPool.classesAccept(grouper) - "Then the amount packages of packages found by the grouper equals the amount of packages in the class pool" { - grouper.size() shouldBe 2 - } - "Then the grouper has found the packages that are in the class pool" { - grouper.containsPackage("app/package1") shouldBe true - grouper.containsPackage("app/package2") shouldBe true - } - "Then the grouper does not contain other packages, except for those from the class pool" { - grouper.packageNames().forEach { - it shouldBeIn arrayListOf("app/package2", "app/package1") - } - } - "Then the union of classes in the packages of the package grouper contain equals the class pool" { - val grouperCompleteClassPool = ClassPool() - grouper.packagesAccept { packageClassPool -> - packageClassPool.classes().forEach { clazz -> - programClassPool.contains(clazz) - grouperCompleteClassPool.addClass(clazz) - } - } - programClassPool.classes().forEach { clazz -> - grouperCompleteClassPool.contains(clazz) - } - } - "Then the classes of a package class pool belong to the respective package" { - grouper.packageNames().forEach { packageName -> - grouper.packageAccept(packageName) { packageClassPool -> - packageClassPool.classesAccept { clazz -> - ClassUtil.internalPackageName(clazz.name) shouldBe packageName - } - } - } - } - } - } -}) From 7d34770bc490c7fd59a90a3c03d8e4f7373b98d3 Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 29 Jun 2022 16:32:43 +0200 Subject: [PATCH 29/32] Move ModifiedAllInnerClassesInfoVisitor from PGC to ProGuard --- .../ModifiedAllInnerClassesInfoVisitor.java | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/main/java/proguard/classfile/attribute/visitor/ModifiedAllInnerClassesInfoVisitor.java diff --git a/src/main/java/proguard/classfile/attribute/visitor/ModifiedAllInnerClassesInfoVisitor.java b/src/main/java/proguard/classfile/attribute/visitor/ModifiedAllInnerClassesInfoVisitor.java deleted file mode 100644 index 310241800..000000000 --- a/src/main/java/proguard/classfile/attribute/visitor/ModifiedAllInnerClassesInfoVisitor.java +++ /dev/null @@ -1,25 +0,0 @@ -package proguard.classfile.attribute.visitor; - -import proguard.classfile.Clazz; -import proguard.classfile.attribute.InnerClassesAttribute; - -/** - * This {@link AllInnerClassesInfoVisitor} revisits each {@link InnerClassesAttribute} everytime its amount of - * referenced classes has been modified in the meantime. - */ -public class ModifiedAllInnerClassesInfoVisitor extends AllInnerClassesInfoVisitor { - - public ModifiedAllInnerClassesInfoVisitor(InnerClassesInfoVisitor innerClassesInfoVisitor) { - super(innerClassesInfoVisitor); - } - - public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) - { - int originalClassesCount = -1; - while (originalClassesCount != innerClassesAttribute.u2classesCount) - { - originalClassesCount = innerClassesAttribute.u2classesCount; - super.visitInnerClassesAttribute(clazz, innerClassesAttribute); - } - } -} From 7441f81326179164c6092731b495659a113f1f6b Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 29 Jun 2022 16:43:22 +0200 Subject: [PATCH 30/32] Move InnerClassRemover to proguard.classfile.editor + update docs --- .../InnerClassRemover.java | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) rename src/main/java/proguard/classfile/{visitor => editor}/InnerClassRemover.java (67%) diff --git a/src/main/java/proguard/classfile/visitor/InnerClassRemover.java b/src/main/java/proguard/classfile/editor/InnerClassRemover.java similarity index 67% rename from src/main/java/proguard/classfile/visitor/InnerClassRemover.java rename to src/main/java/proguard/classfile/editor/InnerClassRemover.java index 655e6a54d..554fcc150 100644 --- a/src/main/java/proguard/classfile/visitor/InnerClassRemover.java +++ b/src/main/java/proguard/classfile/editor/InnerClassRemover.java @@ -1,4 +1,21 @@ -package proguard.classfile.visitor; +/* + * 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; @@ -8,12 +25,17 @@ import proguard.classfile.attribute.InnerClassesInfo; import proguard.classfile.attribute.visitor.AttributeVisitor; import proguard.classfile.attribute.visitor.InnerClassesInfoVisitor; -import proguard.classfile.editor.InnerClassesAttributeEditor; 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; @@ -26,11 +48,11 @@ public InnerClassRemover(Clazz clazz) } @Override - public void visitAnyAttribute(Clazz clazz, Attribute attribute) { - } + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} @Override - public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) { + 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 {}", @@ -42,7 +64,8 @@ public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerC } @Override - public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) { + public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) + { String innerClassName = clazz.getClassName(innerClassesInfo.u2innerClassIndex); if (Objects.equals(innerClassName, this.classToBeRemoved.getName())) { From 082b3a8e1cb2ff01727d333b8d6d5668c58fb7fd Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 29 Jun 2022 16:44:23 +0200 Subject: [PATCH 31/32] Move FieldCopier to ProGuard --- .../classfile/visitor/FieldCopier.java | 78 ------------------- 1 file changed, 78 deletions(-) delete mode 100644 src/main/java/proguard/classfile/visitor/FieldCopier.java diff --git a/src/main/java/proguard/classfile/visitor/FieldCopier.java b/src/main/java/proguard/classfile/visitor/FieldCopier.java deleted file mode 100644 index 5d63627a1..000000000 --- a/src/main/java/proguard/classfile/visitor/FieldCopier.java +++ /dev/null @@ -1,78 +0,0 @@ -package proguard.classfile.visitor; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import proguard.classfile.*; -import proguard.classfile.constant.FieldrefConstant; -import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.editor.ClassBuilder; -import proguard.classfile.editor.ClassEditor; - -public class FieldCopier implements MemberVisitor, ConstantVisitor { - - private final ClassBuilder classBuilder; - private final ClassEditor classEditor; - private final FieldRenamer fieldRenamer; - private ProgramField lastCopiedField; - private boolean hasCopiedField = false; - private static final Logger logger = LogManager.getLogger(FieldCopier.class); - - public FieldCopier(ClassBuilder builder, FieldRenamer renamer) - { - this.classBuilder = builder; - this.classEditor = new ClassEditor(builder.getProgramClass()); - this.fieldRenamer = renamer; - } - - @Override - public void visitProgramField(ProgramClass programClass, ProgramField programField) - { - String fieldName = programField.getName(programClass); - if (this.fieldRenamer != null) - { - fieldName = fieldRenamer.getNextFieldName(); - } - - String fieldDescriptor = programField.getDescriptor(programClass); - Field oldField = classBuilder.getProgramClass().findField(fieldName, null); - Field oldFieldSameDescriptor = classBuilder.getProgramClass().findField(fieldName, fieldDescriptor); - if (oldField != null && oldFieldSameDescriptor == null) - { - String oldFieldDescriptor = oldField.getDescriptor(classBuilder.getProgramClass()); - //logger.warn("Field " + fieldName + " already exists in class " + classBuilder.getProgramClass() + " with different descriptor: " + oldFieldDescriptor + " <-> " + fieldDescriptor + ". The field will be duplicated with different descriptors."); - // Merge the field types: generalise to a common super type - //fieldDescriptor = ClassConstants.TYPE_JAVA_LANG_OBJECT; - } - else if (oldFieldSameDescriptor != null) - { - this.classEditor.removeField(oldFieldSameDescriptor); - } - ProgramField copiedField = classBuilder.addAndReturnField(programField.u2accessFlags, fieldName, fieldDescriptor); - if (this.fieldRenamer != null) - { - this.fieldRenamer.visitProgramField(classBuilder.getProgramClass(), copiedField); - } - this.lastCopiedField = copiedField; - this.hasCopiedField = true; - } - - @Override - public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) { - fieldrefConstant.referencedFieldAccept(this); - } - - public ProgramField getLastCopiedField() - { - return this.lastCopiedField; - } - - public boolean hasCopiedField() - { - return this.hasCopiedField; - } - - public void reset() - { - this.hasCopiedField = false; - } -} From 31f231dede97348e51cdffd479a0152e4ac65c1a Mon Sep 17 00:00:00 2001 From: Joren Van Hecke Date: Wed, 29 Jun 2022 16:48:51 +0200 Subject: [PATCH 32/32] MethodCopierTest moved to ProGuard --- .../classfile/visitor/MethodCopierTest.kt | 105 ------------------ 1 file changed, 105 deletions(-) delete mode 100644 src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt diff --git a/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt b/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt deleted file mode 100644 index e96e436e5..000000000 --- a/src/test/kotlin/proguard/classfile/visitor/MethodCopierTest.kt +++ /dev/null @@ -1,105 +0,0 @@ -package proguard.classfile.visitor - -import io.kotest.core.spec.style.FreeSpec -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import proguard.classfile.AccessConstants -import proguard.classfile.ClassConstants -import proguard.classfile.VersionConstants -import proguard.classfile.attribute.visitor.AllAttributeVisitor -import proguard.classfile.editor.ClassBuilder -import proguard.classfile.editor.InstructionSequenceBuilder -import proguard.classfile.instruction.visitor.AllInstructionVisitor -import proguard.classfile.util.InstructionSequenceMatcher -import testutils.MatchDetector - -class MethodCopierTest : FreeSpec({ - "Given a method and a target class" - { - val classBuilder = ClassBuilder( - VersionConstants.CLASS_VERSION_1_8, - AccessConstants.FINAL or AccessConstants.SUPER, - "Test", - ClassConstants.NAME_JAVA_LANG_OBJECT - ) - val testClass = classBuilder.programClass - val method = classBuilder.addAndReturnMethod( - AccessConstants.PUBLIC, - ClassConstants.METHOD_NAME_INIT, - ClassConstants.METHOD_TYPE_INIT, - 50 - ) { - it - .aload_0() - .iconst(0) - .invokespecial( - ClassConstants.NAME_JAVA_LANG_OBJECT, - ClassConstants.METHOD_NAME_INIT, - ClassConstants.METHOD_TYPE_INIT - ) - .return_() - } - val methodDescriptor = method.getDescriptor(testClass) - val methodName = method.getName(testClass) - val methodAccessFlags = AccessConstants.PUBLIC - val targetClass = ClassBuilder( - VersionConstants.CLASS_VERSION_1_8, - AccessConstants.PUBLIC, - "TargetClass", - ClassConstants.NAME_JAVA_LANG_OBJECT - ).programClass - "When the method is copied to the target class" - { - val methodCopier = MethodCopier(targetClass, ClassConstants.METHOD_NAME_INIT, methodAccessFlags) - method.accept(testClass, methodCopier) - "Then the target class contains a method with the correct name and descriptor" { - targetClass.findMethod(methodName, methodDescriptor) shouldNotBe null - } - - "Then the copied method has the correct access modifiers" { - val copiedMethod = targetClass.findMethod(methodName, methodDescriptor) - copiedMethod.accessFlags shouldBe methodAccessFlags - } - - "Then the instructions of the copied method match those of the original method" { - val builder = InstructionSequenceBuilder() - builder - .aload_0() - .iconst(0) - .invokespecial( - ClassConstants.NAME_JAVA_LANG_OBJECT, - ClassConstants.METHOD_NAME_INIT, - ClassConstants.METHOD_TYPE_INIT - ) - .return_() - val matchDetector = MatchDetector(InstructionSequenceMatcher(builder.constants(), builder.instructions())) - val copiedMethod = targetClass.findMethod(methodName, methodDescriptor) - copiedMethod.accept( - targetClass, - AllAttributeVisitor( - AllInstructionVisitor( - matchDetector - ) - ) - ) - matchDetector.matchIsFound shouldBe true - } - } - - "When the method is copied to the target class with a new descriptor" - { - val newDescriptor = "(I)V" - val methodCopier = MethodCopier(targetClass, methodName, newDescriptor, methodAccessFlags) - method.accept(testClass, methodCopier) - "Then the target class contains a method with the correct name and descriptor" { - targetClass.findMethod(methodName, newDescriptor) shouldNotBe null - } - } - - "When the method is copied to the target class with a new name prefix" - { - val newNamePrefix = "copiedMethod" - val methodCopier = MethodCopier(targetClass, newNamePrefix, methodAccessFlags) - method.accept(testClass, methodCopier) - "Then the target class contains a method with the correct name and descriptor" { - targetClass.findMethod(newNamePrefix, methodDescriptor) shouldNotBe null - } - } - } -})