From 9b8f80229a89e327a4cb2c2c4ffc1460eff39cd5 Mon Sep 17 00:00:00 2001 From: James Hamilton Date: Fri, 9 Dec 2022 16:14:13 +0100 Subject: [PATCH] Set the `IS_CLASS_AVAILABLE` processing flag --- base/src/main/java/proguard/mark/Marker.java | 78 +++++++++-- .../optimize/MemberDescriptorSpecializer.java | 25 ++-- .../MemberDescriptorSpecializerTest.kt | 125 ++++++++++++++++++ docs/md/manual/releasenotes.md | 1 + 4 files changed, 212 insertions(+), 17 deletions(-) create mode 100644 base/src/test/kotlin/proguard/optimize/MemberDescriptorSpecializerTest.kt diff --git a/base/src/main/java/proguard/mark/Marker.java b/base/src/main/java/proguard/mark/Marker.java index a9ff860a..39d52645 100644 --- a/base/src/main/java/proguard/mark/Marker.java +++ b/base/src/main/java/proguard/mark/Marker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2020 Guardsquare NV + * Copyright (c) 2002-2022 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 @@ -22,21 +22,56 @@ package proguard.mark; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import proguard.*; -import proguard.classfile.*; +import proguard.AppView; +import proguard.Configuration; +import proguard.KeepClassSpecificationVisitorFactory; +import proguard.classfile.AccessConstants; +import proguard.classfile.ClassConstants; +import proguard.classfile.ClassPool; +import proguard.classfile.Clazz; import proguard.classfile.attribute.Attribute; -import proguard.classfile.attribute.visitor.*; -import proguard.classfile.kotlin.*; -import proguard.classfile.kotlin.visitor.*; +import proguard.classfile.attribute.visitor.AllAttributeVisitor; +import proguard.classfile.attribute.visitor.AttributeNameFilter; +import proguard.classfile.attribute.visitor.AttributeProcessingFlagFilter; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.kotlin.KotlinClassKindMetadata; +import proguard.classfile.kotlin.KotlinConstants; +import proguard.classfile.kotlin.KotlinDeclarationContainerMetadata; +import proguard.classfile.kotlin.KotlinFunctionMetadata; +import proguard.classfile.kotlin.KotlinMetadata; +import proguard.classfile.kotlin.KotlinSyntheticClassKindMetadata; +import proguard.classfile.kotlin.visitor.KotlinFunctionToDefaultMethodVisitor; +import proguard.classfile.kotlin.visitor.KotlinFunctionToMethodVisitor; +import proguard.classfile.kotlin.visitor.KotlinFunctionVisitor; +import proguard.classfile.kotlin.visitor.KotlinMetadataVisitor; +import proguard.classfile.kotlin.visitor.ReferencedKotlinMetadataVisitor; import proguard.classfile.util.AllParameterVisitor; -import proguard.classfile.visitor.*; +import proguard.classfile.visitor.AllMemberVisitor; +import proguard.classfile.visitor.ClassAccessFilter; +import proguard.classfile.visitor.ClassNameFilter; +import proguard.classfile.visitor.ClassPoolVisitor; +import proguard.classfile.visitor.ClassProcessingFlagFilter; +import proguard.classfile.visitor.ClassVisitor; +import proguard.classfile.visitor.MemberAccessFilter; +import proguard.classfile.visitor.MemberDescriptorReferencedClassVisitor; +import proguard.classfile.visitor.MemberNameFilter; +import proguard.classfile.visitor.MemberProcessingFlagFilter; +import proguard.classfile.visitor.MemberToClassVisitor; +import proguard.classfile.visitor.MemberVisitor; +import proguard.classfile.visitor.MultiClassPoolVisitor; +import proguard.classfile.visitor.MultiClassVisitor; +import proguard.classfile.visitor.MultiMemberVisitor; +import proguard.classfile.visitor.NamedMethodVisitor; import proguard.pass.Pass; -import proguard.util.*; +import proguard.util.ProcessingFlagSetter; +import proguard.util.ProcessingFlags; import java.util.Arrays; -import static proguard.util.ProcessingFlags.*; import static proguard.util.ProcessingFlags.DONT_OBFUSCATE; +import static proguard.util.ProcessingFlags.DONT_OPTIMIZE; +import static proguard.util.ProcessingFlags.DONT_SHRINK; +import static proguard.util.ProcessingFlags.INJECTED; /** * This pass translates the keep rules and other class specifications from the @@ -91,6 +126,13 @@ public class Marker implements Pass appView.libraryClassPool.classesAccept(classVisitor); } + // Mark members that can be safely used for generalization, + // but only if optimization is enabled. + if (configuration.optimize) + { + markSafeGeneralizationMembers(appView.programClassPool, appView.libraryClassPool); + } + if (configuration.keepKotlinMetadata) { disableOptimizationForKotlinFeatures(appView.programClassPool, appView.libraryClassPool); @@ -187,6 +229,24 @@ public class Marker implements Pass } + private void markSafeGeneralizationMembers(ClassPool programClassPool, + ClassPool libraryClassPool) + { + // Program classes are always available and safe to generalize/specialize from/to. + ClassVisitor isClassAvailableMarker = + new AllMemberVisitor( + new ProcessingFlagSetter(ProcessingFlags.IS_CLASS_AVAILABLE)); + + programClassPool.classesAccept(isClassAvailableMarker); + + if (!configuration.optimizeConservatively) + { + libraryClassPool.classesAccept(isClassAvailableMarker); + } + // TODO: Mark library class members where appropriate in the conservative case. + } + + /** * This method will disable optimization for all Kotlin components where required, * such as for $default methods. diff --git a/base/src/main/java/proguard/optimize/MemberDescriptorSpecializer.java b/base/src/main/java/proguard/optimize/MemberDescriptorSpecializer.java index 72aedf86..042f0f79 100644 --- a/base/src/main/java/proguard/optimize/MemberDescriptorSpecializer.java +++ b/base/src/main/java/proguard/optimize/MemberDescriptorSpecializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2021 Guardsquare NV + * Copyright (c) 2002-2022 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 @@ -22,9 +22,18 @@ package proguard.optimize; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import proguard.classfile.*; -import proguard.classfile.editor.*; -import proguard.classfile.util.*; +import proguard.classfile.AccessConstants; +import proguard.classfile.ClassConstants; +import proguard.classfile.Clazz; +import proguard.classfile.ProgramClass; +import proguard.classfile.ProgramField; +import proguard.classfile.ProgramMember; +import proguard.classfile.ProgramMethod; +import proguard.classfile.TypeConstants; +import proguard.classfile.editor.ConstantPoolEditor; +import proguard.classfile.editor.MemberReferenceFixer; +import proguard.classfile.util.ClassUtil; +import proguard.classfile.util.InternalTypeEnumeration; import proguard.classfile.visitor.MemberVisitor; import proguard.evaluation.value.Value; import proguard.optimize.evaluation.StoringInvocationUnit; @@ -132,7 +141,7 @@ implements MemberVisitor if (valueClass != null && valueClass.extendsOrImplements(ClassUtil.internalClassNameFromClassType(fieldType)) && - (valueClass.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0) + (programField.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0) { logger.debug("MemberDescriptorSpecializer [{}.{} {}] -> {}", programClass.getName(), @@ -179,7 +188,7 @@ implements MemberVisitor InternalTypeEnumeration parameterTypeEnumeration = new InternalTypeEnumeration(descriptor); - StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length()); + StringBuilder newDescriptorBuffer = new StringBuilder(descriptor.length()); newDescriptorBuffer.append(TypeConstants.METHOD_ARGUMENTS_OPEN); while (parameterTypeEnumeration.hasMoreTypes()) @@ -201,7 +210,7 @@ implements MemberVisitor if (valueClass != null && valueClass.extendsOrImplements(ClassUtil.internalClassNameFromClassType(parameterType)) && - (valueClass.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0) + (programMethod.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0) { logger.debug("MemberDescriptorSpecializer [{}.{}{}]: parameter #{}: {} -> {}", programClass.getName(), @@ -318,7 +327,7 @@ implements MemberVisitor return type; } - return ClassUtil.internalTypeFromClassType(valueType); + return valueType; } diff --git a/base/src/test/kotlin/proguard/optimize/MemberDescriptorSpecializerTest.kt b/base/src/test/kotlin/proguard/optimize/MemberDescriptorSpecializerTest.kt new file mode 100644 index 00000000..0e1cbdbf --- /dev/null +++ b/base/src/test/kotlin/proguard/optimize/MemberDescriptorSpecializerTest.kt @@ -0,0 +1,125 @@ +package proguard.optimize + +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe +import proguard.classfile.AccessConstants +import proguard.classfile.Clazz +import proguard.classfile.Member +import proguard.classfile.attribute.visitor.AllAttributeVisitor +import proguard.classfile.attribute.visitor.DebugAttributeVisitor +import proguard.classfile.visitor.AllMemberVisitor +import proguard.classfile.visitor.AllMethodVisitor +import proguard.classfile.visitor.ClassAccessFilter +import proguard.classfile.visitor.MemberNameFilter +import proguard.classfile.visitor.MemberVisitor +import proguard.classfile.visitor.ParallelAllClassVisitor.ClassVisitorFactory +import proguard.evaluation.InvocationUnit +import proguard.evaluation.PartialEvaluator +import proguard.evaluation.value.ParticularValueFactory +import proguard.evaluation.value.ValueFactory +import proguard.optimize.evaluation.StoringInvocationUnit +import proguard.optimize.info.ProgramClassOptimizationInfoSetter +import proguard.optimize.info.ProgramMemberOptimizationInfoSetter +import proguard.testutils.ClassPoolBuilder +import proguard.testutils.JavaSource +import proguard.util.ProcessingFlagSetter +import proguard.util.ProcessingFlags.IS_CLASS_AVAILABLE + +class MemberDescriptorSpecializerTest : FreeSpec({ + "Given a method with a more general parameter type than its use" - { + val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource( + JavaSource( + "Test.java", + """ + public class Test { + public static void main(String[] args) { + foo(new Foo()); + } + public static void foo(Bar foo) { + System.out.println(foo); + } + } + + class Bar { } + + class Foo extends Bar { } + """.trimIndent() + ) + ) + + "When specializing the member descriptors" - { + + // Mark all members as available. + programClassPool.classesAccept(AllMemberVisitor(ProcessingFlagSetter(IS_CLASS_AVAILABLE))) + + // Setup the OptimizationInfo on the classes + val keepMarker = KeepMarker() + libraryClassPool.classesAccept(keepMarker) + libraryClassPool.classesAccept(AllMemberVisitor(keepMarker)) + + programClassPool.classesAccept(ProgramClassOptimizationInfoSetter()) + programClassPool.classesAccept(AllMemberVisitor(ProgramMemberOptimizationInfoSetter())) + + // Create the optimization as in Optimizer + val fillingOutValuesClassVisitor = ClassVisitorFactory { + val valueFactory: ValueFactory = ParticularValueFactory() + val storingInvocationUnit: InvocationUnit = StoringInvocationUnit( + valueFactory, + true, + true, + true + ) + ClassAccessFilter( + 0, AccessConstants.SYNTHETIC, + AllMethodVisitor( + AllAttributeVisitor( + DebugAttributeVisitor( + "Filling out fields, method parameters, and return values", + PartialEvaluator( + valueFactory, storingInvocationUnit, + true + ) + ) + ) + ) + ) + } + + programClassPool.classesAccept(fillingOutValuesClassVisitor.createClassVisitor()) + + // Specialize class member descriptors, based on partial evaluation. + programClassPool.classesAccept( + AllMemberVisitor( + OptimizationInfoMemberFilter( + MemberDescriptorSpecializer( + true, + true, + true, + null, + null, + null + ) + ) + ) + ) + + "Then the member descriptor should be correctly specialised" { + lateinit var memberDescriptor: String + programClassPool.classAccept( + "Test", + AllMemberVisitor( + MemberNameFilter( + "foo*", + object : MemberVisitor { + override fun visitAnyMember(clazz: Clazz, member: Member) { + memberDescriptor = member.getDescriptor(clazz) + } + } + ) + ) + ) + memberDescriptor shouldBe "(LFoo;)V" + } + } + } +}) diff --git a/docs/md/manual/releasenotes.md b/docs/md/manual/releasenotes.md index 0e51e75d..f2079e54 100644 --- a/docs/md/manual/releasenotes.md +++ b/docs/md/manual/releasenotes.md @@ -11,6 +11,7 @@ - Fix `-keepparameternames` to keep Kotlin interface parameter names. - Fix potential `NullPointerException` while processing enum classes with invalid Kotlin metadata. - Fix potential `Instruction has invalid constant index size` error during GSON optimization. +- Fix member specialization & generalization optimizations. ## Version 7.3.0