Set the IS_CLASS_AVAILABLE processing flag

This commit is contained in:
James Hamilton
2022-12-09 16:14:13 +01:00
committed by Tim Van Den Broecke
parent 1cbd6f7a68
commit 9b8f80229a
4 changed files with 212 additions and 17 deletions

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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"
}
}
}
})

View File

@@ -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