mirror of
https://github.com/Guardsquare/proguard.git
synced 2026-03-13 09:50:34 +08:00
Compare commits
2 Commits
v7.7
...
349-verify
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddcb041b08 | ||
|
|
67a658e3d7 |
@@ -36,6 +36,8 @@ import proguard.evaluation.PartialEvaluator;
|
||||
import proguard.evaluation.value.*;
|
||||
import proguard.optimize.info.SimpleEnumMarker;
|
||||
|
||||
import static proguard.classfile.ClassConstants.NAME_JAVA_LANG_OBJECT;
|
||||
|
||||
/**
|
||||
* This AttributeVisitor simplifies the use of enums in the code attributes that
|
||||
* it visits.
|
||||
@@ -277,6 +279,15 @@ implements AttributeVisitor,
|
||||
invokedMethodName,
|
||||
invokedMethodType);
|
||||
}
|
||||
else if (isObjectCloneCallPoppingSimpleEnumArray(
|
||||
offset,
|
||||
stackEntryIndex,
|
||||
invokedMethodName,
|
||||
invokedMethodType,
|
||||
clazz.getRefClassName(constantInstruction.constantIndex)))
|
||||
{
|
||||
replaceObjectCloneCall(clazz, offset, constantInstruction, invokedMethodName, invokedMethodType);
|
||||
}
|
||||
|
||||
// Fall through to check the parameters.
|
||||
}
|
||||
@@ -567,6 +578,26 @@ implements AttributeVisitor,
|
||||
ClassUtil.internalArrayTypeDimensionCount(referenceValue.getType()) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the instruction at the given offset is popping a simple enum array
|
||||
* and the class reference is java.lang.Object.
|
||||
* <p>
|
||||
* This accounts for the Kotlin compiler generating the following call in the `values()` method:
|
||||
* <code>
|
||||
* invokevirtual [Ljava/lang/Object;->clone();
|
||||
* </code>
|
||||
* where the Java compiler would generate:
|
||||
* <code>
|
||||
* invokevirtual [LMyEnum;->clone();
|
||||
* </code>
|
||||
*/
|
||||
private boolean isObjectCloneCallPoppingSimpleEnumArray(int offset, int stackEntryIndex, String invokedMethodName, String invokedMethodType, String classRef)
|
||||
{
|
||||
return invokedMethodName.equals("clone") &&
|
||||
invokedMethodType.equals("()Ljava/lang/Object;") &&
|
||||
ClassUtil.internalClassNameFromClassType(classRef).equals(NAME_JAVA_LANG_OBJECT) &&
|
||||
isPoppingSimpleEnumArray(offset, stackEntryIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given class is not null and a simple enum class.
|
||||
@@ -603,6 +634,29 @@ implements AttributeVisitor,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a Kotlin generated clone() invocation with the correct integer array invocation.
|
||||
*/
|
||||
private void replaceObjectCloneCall(Clazz clazz,
|
||||
int offset,
|
||||
Instruction instruction,
|
||||
String name,
|
||||
String type)
|
||||
{
|
||||
if (name.equals("clone") && type.equals("()Ljava/lang/Object;"))
|
||||
{
|
||||
ConstantPoolEditor cpe = new ConstantPoolEditor((ProgramClass) clazz);
|
||||
Instruction[] replacementInstructions = new Instruction[] {
|
||||
new ConstantInstruction(
|
||||
Instruction.OP_INVOKEVIRTUAL,
|
||||
cpe.addMethodrefConstant("[I", "clone", "()Ljava/lang/Object;", null, null)
|
||||
)
|
||||
};
|
||||
|
||||
replaceInstructions(clazz, offset, instruction, replacementInstructions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces the instruction at the given offset by the given instructions.
|
||||
|
||||
109
base/src/test/kotlin/proguard/optimize/EnumSimplifierTest.kt
Normal file
109
base/src/test/kotlin/proguard/optimize/EnumSimplifierTest.kt
Normal file
@@ -0,0 +1,109 @@
|
||||
package proguard.optimize
|
||||
|
||||
import io.kotest.core.spec.style.FreeSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import proguard.classfile.ClassPool
|
||||
import proguard.classfile.ProgramClass
|
||||
import proguard.classfile.ProgramMember
|
||||
import proguard.classfile.attribute.visitor.AllAttributeVisitor
|
||||
import proguard.classfile.editor.MemberReferenceFixer
|
||||
import proguard.classfile.util.ClassPoolClassLoader
|
||||
import proguard.classfile.visitor.AllMemberVisitor
|
||||
import proguard.classfile.visitor.AllMethodVisitor
|
||||
import proguard.classfile.visitor.MemberNameFilter
|
||||
import proguard.classfile.visitor.MemberVisitor
|
||||
import proguard.optimize.evaluation.SimpleEnumClassSimplifier
|
||||
import proguard.optimize.evaluation.SimpleEnumDescriptorSimplifier
|
||||
import proguard.optimize.evaluation.SimpleEnumUseSimplifier
|
||||
import proguard.optimize.info.ProgramClassOptimizationInfoSetter
|
||||
import proguard.optimize.info.ProgramMemberOptimizationInfoSetter
|
||||
import proguard.optimize.info.SimpleEnumFilter
|
||||
import proguard.optimize.info.SimpleEnumMarker
|
||||
import proguard.testutils.ClassPoolBuilder
|
||||
import proguard.testutils.JavaSource
|
||||
import proguard.testutils.KotlinSource
|
||||
import proguard.testutils.and
|
||||
import proguard.testutils.match
|
||||
|
||||
class EnumSimplifierTest : FreeSpec({
|
||||
"Test simplification of simple Kotlin enum (#349)" {
|
||||
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
|
||||
KotlinSource(
|
||||
"TestEnum.kt", "enum class TestEnum { A, B, C }"
|
||||
)
|
||||
)
|
||||
|
||||
simplifyEnum(libraryClassPool, programClassPool)
|
||||
}
|
||||
|
||||
"Test simplification of simple Java enum" {
|
||||
val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(
|
||||
JavaSource(
|
||||
"TestEnum.java", "public enum TestEnum { A, B, C, }"
|
||||
)
|
||||
)
|
||||
|
||||
simplifyEnum(libraryClassPool, programClassPool)
|
||||
}
|
||||
})
|
||||
|
||||
private fun simplifyEnum(libraryClassPool: ClassPool, programClassPool: ClassPool) {
|
||||
// Setup the OptimizationInfo on the classes
|
||||
val keepMarker = KeepMarker()
|
||||
libraryClassPool.classesAccept(keepMarker)
|
||||
libraryClassPool.classesAccept(AllMemberVisitor(keepMarker))
|
||||
programClassPool.classesAccept(ProgramClassOptimizationInfoSetter())
|
||||
programClassPool.classesAccept(AllMemberVisitor(ProgramMemberOptimizationInfoSetter()))
|
||||
|
||||
programClassPool.classAccept("TestEnum", SimpleEnumMarker(true))
|
||||
|
||||
val enumClazz = programClassPool.getClass("TestEnum")
|
||||
|
||||
SimpleEnumMarker.isSimpleEnum(enumClazz) shouldBe true
|
||||
|
||||
// Application of enum simplification similar to the Optimizer.
|
||||
|
||||
// Simplify the use of the enum classes in code.
|
||||
programClassPool.classesAccept(
|
||||
AllMethodVisitor(
|
||||
AllAttributeVisitor(
|
||||
SimpleEnumUseSimplifier()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Simplify the static initializers of simple enum classes.
|
||||
programClassPool.classesAccept(
|
||||
SimpleEnumFilter(
|
||||
SimpleEnumClassSimplifier()
|
||||
)
|
||||
)
|
||||
|
||||
// Simplify the use of the enum classes in descriptors.
|
||||
programClassPool.classesAccept(
|
||||
SimpleEnumDescriptorSimplifier()
|
||||
)
|
||||
|
||||
// Update references to class members with simple enum classes.
|
||||
programClassPool.classesAccept(MemberReferenceFixer(false))
|
||||
|
||||
// Trigger class loading, that would previously result in a verify error.
|
||||
val classPoolClassLoader = ClassPoolClassLoader(programClassPool)
|
||||
val clazz = classPoolClassLoader.findClass("TestEnum")
|
||||
clazz.fields.single { it.name.startsWith("A") }.get(null) shouldBe 1
|
||||
|
||||
enumClazz.methodsAccept(
|
||||
MemberNameFilter(
|
||||
"values*",
|
||||
object : MemberVisitor {
|
||||
override fun visitProgramMember(programClass: ProgramClass, programMember: ProgramMember) {
|
||||
with(programClass and programMember) {
|
||||
match {
|
||||
invokevirtual("[I", "clone", "()Ljava/lang/Object;")
|
||||
} shouldBe true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
### Bugfixes
|
||||
|
||||
- Fix "NoClassDefFoundError: Failed resolution of: Lorg/apache/logging/log4j/LogManager" when using GSON optimization or `-addconfigurationdebugging`. (#326)
|
||||
- Fix "VerifyError" when optimizing Kotlin simple enums. (#349)
|
||||
|
||||
## Version 7.3.2
|
||||
|
||||
|
||||
Reference in New Issue
Block a user