mirror of
https://github.com/Guardsquare/proguard.git
synced 2026-03-13 09:50:34 +08:00
Fix MemberDescriptorSpecializer checking wrong processing flags
This diff also ensures the `IS_CLASS_AVAILABLE` processing flag is set on classes instead of its members. This requires us to change usages of this flag in some other places as well.
This commit is contained in:
@@ -34,7 +34,7 @@ dependencies {
|
||||
testImplementation 'io.kotest:kotest-property-jvm:5.5.4' // for kotest property test
|
||||
testImplementation 'io.mockk:mockk:1.13.2' // for mocking
|
||||
|
||||
testImplementation(testFixtures("com.guardsquare:proguard-core:9.0.8")) {
|
||||
testImplementation(testFixtures("com.guardsquare:proguard-core:${proguardCoreVersion}")) {
|
||||
exclude group: 'com.guardsquare', module: 'proguard-core'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,8 +234,7 @@ public class Marker implements Pass
|
||||
{
|
||||
// Program classes are always available and safe to generalize/specialize from/to.
|
||||
ClassVisitor isClassAvailableMarker =
|
||||
new AllMemberVisitor(
|
||||
new ProcessingFlagSetter(ProcessingFlags.IS_CLASS_AVAILABLE));
|
||||
new ProcessingFlagSetter(ProcessingFlags.IS_CLASS_AVAILABLE);
|
||||
|
||||
programClassPool.classesAccept(isClassAvailableMarker);
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ implements MemberVisitor
|
||||
|
||||
if (valueClass != null &&
|
||||
valueClass.extendsOrImplements(ClassUtil.internalClassNameFromClassType(fieldType)) &&
|
||||
(programField.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
(valueClass.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
{
|
||||
logger.debug("MemberDescriptorSpecializer [{}.{} {}] -> {}",
|
||||
programClass.getName(),
|
||||
@@ -210,7 +210,7 @@ implements MemberVisitor
|
||||
|
||||
if (valueClass != null &&
|
||||
valueClass.extendsOrImplements(ClassUtil.internalClassNameFromClassType(parameterType)) &&
|
||||
(programMethod.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
(valueClass.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
{
|
||||
logger.debug("MemberDescriptorSpecializer [{}.{}{}]: parameter #{}: {} -> {}",
|
||||
programClass.getName(),
|
||||
|
||||
@@ -22,12 +22,22 @@ package proguard.optimize;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import proguard.classfile.*;
|
||||
import proguard.classfile.AccessConstants;
|
||||
import proguard.classfile.Clazz;
|
||||
import proguard.classfile.Field;
|
||||
import proguard.classfile.Member;
|
||||
import proguard.classfile.Method;
|
||||
import proguard.classfile.ProgramClass;
|
||||
import proguard.classfile.attribute.CodeAttribute;
|
||||
import proguard.classfile.constant.*;
|
||||
import proguard.classfile.constant.AnyMethodrefConstant;
|
||||
import proguard.classfile.constant.ClassConstant;
|
||||
import proguard.classfile.constant.FieldrefConstant;
|
||||
import proguard.classfile.constant.RefConstant;
|
||||
import proguard.classfile.constant.visitor.ConstantVisitor;
|
||||
import proguard.classfile.editor.*;
|
||||
import proguard.classfile.instruction.*;
|
||||
import proguard.classfile.editor.CodeAttributeEditor;
|
||||
import proguard.classfile.editor.ConstantPoolEditor;
|
||||
import proguard.classfile.instruction.ConstantInstruction;
|
||||
import proguard.classfile.instruction.Instruction;
|
||||
import proguard.classfile.instruction.visitor.InstructionVisitor;
|
||||
import proguard.classfile.visitor.ClassVisitor;
|
||||
import proguard.util.ProcessingFlags;
|
||||
@@ -200,7 +210,7 @@ implements InstructionVisitor,
|
||||
// DGD-486: Only generalize members which are always available. Partial replacement of a class that is not
|
||||
// available on all platforms may result in a VerifyError at runtime.
|
||||
if (referencedMember != null &&
|
||||
(referencedMember.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
(clazz.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
{
|
||||
clazz.constantPoolEntryAccept(refConstant.u2classIndex, this);
|
||||
|
||||
@@ -287,13 +297,14 @@ implements InstructionVisitor,
|
||||
// Otherwise, look in the super class itself.
|
||||
// Only consider public classes and methods, to avoid any
|
||||
// access problems.
|
||||
// Only consider classes that are marked as available.
|
||||
if (generalizedClass == null &&
|
||||
(superClass.getAccessFlags() & AccessConstants.PUBLIC) != 0)
|
||||
(superClass.getAccessFlags() & AccessConstants.PUBLIC) != 0 &&
|
||||
(superClass.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
{
|
||||
Method method = superClass.findMethod(memberName, memberType);
|
||||
if (method != null &&
|
||||
(method.getAccessFlags() & AccessConstants.PUBLIC) != 0 &&
|
||||
(method.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
(method.getAccessFlags() & AccessConstants.PUBLIC) != 0)
|
||||
{
|
||||
// Remember the generalized class and class member.
|
||||
generalizedClass = superClass;
|
||||
@@ -308,7 +319,7 @@ implements InstructionVisitor,
|
||||
Field field = clazz.findField(memberName, memberType);
|
||||
|
||||
if (field != null &&
|
||||
(field.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
(clazz.getProcessingFlags() & ProcessingFlags.IS_CLASS_AVAILABLE) != 0)
|
||||
{
|
||||
// Remember the generalized class and class member.
|
||||
generalizedClass = clazz;
|
||||
|
||||
@@ -3,6 +3,7 @@ package proguard.optimize
|
||||
import io.kotest.core.spec.style.FreeSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import proguard.classfile.AccessConstants
|
||||
import proguard.classfile.ClassPool
|
||||
import proguard.classfile.Clazz
|
||||
import proguard.classfile.Member
|
||||
import proguard.classfile.attribute.visitor.AllAttributeVisitor
|
||||
@@ -26,7 +27,67 @@ 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" - {
|
||||
|
||||
fun specializeMemberDescriptors(
|
||||
programClassPool: ClassPool,
|
||||
libraryClassPool: ClassPool,
|
||||
) {
|
||||
// Mark all program classes as available.
|
||||
programClassPool.classesAccept(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
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"Given a method with a more general program class pool parameter type than its use" - {
|
||||
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
|
||||
JavaSource(
|
||||
"Test.java",
|
||||
@@ -48,60 +109,7 @@ class MemberDescriptorSpecializerTest : FreeSpec({
|
||||
)
|
||||
|
||||
"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
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
specializeMemberDescriptors(programClassPool, libraryClassPool)
|
||||
|
||||
"Then the member descriptor should be correctly specialised" {
|
||||
lateinit var memberDescriptor: String
|
||||
@@ -122,4 +130,87 @@ class MemberDescriptorSpecializerTest : FreeSpec({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"Given a field with a more general program class pool parameter type than its use" - {
|
||||
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
|
||||
JavaSource(
|
||||
"Test.java",
|
||||
"""
|
||||
public class Test {
|
||||
static Bar myField = null;
|
||||
|
||||
public static void main(String[] args) {
|
||||
myField = new Foo();
|
||||
}
|
||||
}
|
||||
|
||||
class Bar { }
|
||||
|
||||
class Foo extends Bar { }
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
|
||||
"When specializing the member descriptors" - {
|
||||
specializeMemberDescriptors(programClassPool, libraryClassPool)
|
||||
|
||||
"Then the member descriptor should be correctly specialised" {
|
||||
lateinit var memberDescriptor: String
|
||||
programClassPool.classAccept(
|
||||
"Test",
|
||||
AllMemberVisitor(
|
||||
MemberNameFilter(
|
||||
"myField*",
|
||||
object : MemberVisitor {
|
||||
override fun visitAnyMember(clazz: Clazz, member: Member) {
|
||||
memberDescriptor = member.getDescriptor(clazz)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
memberDescriptor shouldBe "LFoo;"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"Given a field with a more general library class pool parameter type than its use" - {
|
||||
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
|
||||
JavaSource(
|
||||
"Test.java",
|
||||
"""
|
||||
public class Test {
|
||||
static java.lang.Object myField = null;
|
||||
|
||||
public static void main(String[] args) {
|
||||
myField = new java.lang.StringBuffer();
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
|
||||
"When specializing the member descriptors" - {
|
||||
specializeMemberDescriptors(programClassPool, libraryClassPool)
|
||||
|
||||
"Then the member descriptor should be correctly specialised" {
|
||||
lateinit var memberDescriptor: String
|
||||
programClassPool.classAccept(
|
||||
"Test",
|
||||
AllMemberVisitor(
|
||||
MemberNameFilter(
|
||||
"myField*",
|
||||
object : MemberVisitor {
|
||||
override fun visitAnyMember(clazz: Clazz, member: Member) {
|
||||
memberDescriptor = member.getDescriptor(clazz)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
// Library classes are not marked as available by default. Therefore, they are not specialized.
|
||||
memberDescriptor shouldBe "Ljava/lang/Object;"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
### Bugfixes
|
||||
|
||||
- Fix potential access issues when backporting.
|
||||
- Fix potential NoClassDefFoundError when using type specialization optimization. (#373)
|
||||
|
||||
## Version 7.4.1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user