mirror of
https://github.com/Guardsquare/proguard.git
synced 2026-03-13 09:50:34 +08:00
Allow method from interfaces to be inlined if it is private and is being called from within the interface
This commit is contained in:
committed by
Dimitrios Anyfantakis
parent
8bb7cc0c4b
commit
7429219cd2
@@ -654,11 +654,12 @@ implements AttributeVisitor,
|
||||
|
||||
DEBUG("Interface?") &&
|
||||
|
||||
// Methods in interfaces should not be inlined since this can potentially
|
||||
// lead to other methods in the interface needing broadened visibility,
|
||||
// which can lead to either compilation errors during output writing
|
||||
// or various issues at runtime.
|
||||
(programClass.getAccessFlags() & AccessConstants.INTERFACE) == 0 &&
|
||||
// Methods in interfaces should only very rarely be inlined
|
||||
// since this can potentially lead to other methods in the interface
|
||||
// needing broadened visibility, which can lead to either compilation errors
|
||||
// during output writing or various issues at runtime.
|
||||
((programClass.getAccessFlags() & AccessConstants.INTERFACE) == 0 ||
|
||||
canInlineMethodFromInterface(programClass, programMethod)) &&
|
||||
|
||||
DEBUG("Synchronized?") &&
|
||||
|
||||
@@ -897,6 +898,17 @@ implements AttributeVisitor,
|
||||
returnChar == TypeConstants.SHORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* We only inline methods from interfaces if both of these conditions hold:
|
||||
* - The class that references the method is the interface itself.
|
||||
* - The method is private.
|
||||
*/
|
||||
private boolean canInlineMethodFromInterface(ProgramClass sourceClass, ProgramMethod sourceMethod)
|
||||
{
|
||||
return
|
||||
sourceClass.equals(targetClass) &&
|
||||
(sourceMethod.getAccessFlags() & AccessConstants.PRIVATE) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this method should be inlined. Subclasses can overwrite
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package proguard.optimize.peephole
|
||||
|
||||
import io.kotest.core.spec.IsolationMode
|
||||
import io.kotest.core.spec.style.FreeSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||
import proguard.classfile.Clazz
|
||||
import proguard.classfile.Method
|
||||
import proguard.classfile.ProgramClass
|
||||
import proguard.classfile.ProgramMethod
|
||||
import proguard.classfile.attribute.CodeAttribute
|
||||
import proguard.classfile.attribute.visitor.AllAttributeVisitor
|
||||
import proguard.classfile.visitor.AllMethodVisitor
|
||||
import proguard.classfile.visitor.ClassVisitor
|
||||
import proguard.classfile.visitor.MultiClassVisitor
|
||||
import proguard.optimize.info.ProgramClassOptimizationInfoSetter
|
||||
import proguard.optimize.info.ProgramMemberOptimizationInfoSetter
|
||||
import proguard.testutils.ClassPoolBuilder
|
||||
import proguard.testutils.JavaSource
|
||||
import testutils.RequiresJavaVersion
|
||||
|
||||
@RequiresJavaVersion(9)
|
||||
class MethodInlinerJava9Test : FreeSpec({
|
||||
isolationMode = IsolationMode.InstancePerTest
|
||||
|
||||
"Given a method calling a private method in the same interface" - {
|
||||
val (programClassPool, _) = ClassPoolBuilder.fromSource(
|
||||
JavaSource(
|
||||
"Foo.java",
|
||||
"""interface Foo {
|
||||
default void f1() {
|
||||
f2();
|
||||
}
|
||||
|
||||
private static void f2() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(System.currentTimeMillis());
|
||||
System.out.println(sb.toString());
|
||||
}
|
||||
}""",
|
||||
)
|
||||
)
|
||||
|
||||
val clazz = programClassPool.getClass("Foo") as ProgramClass
|
||||
val method = clazz.findMethod("f1", "()V") as ProgramMethod
|
||||
val codeAttr = method.attributes.filterIsInstance<CodeAttribute>()[0]
|
||||
|
||||
val lengthBefore = codeAttr.u4codeLength
|
||||
|
||||
// Initialize optimization info (used when inlining).
|
||||
val optimizationInfoInitializer: ClassVisitor = MultiClassVisitor(
|
||||
ProgramClassOptimizationInfoSetter(),
|
||||
AllMethodVisitor(
|
||||
ProgramMemberOptimizationInfoSetter()
|
||||
)
|
||||
)
|
||||
|
||||
programClassPool.classesAccept(optimizationInfoInitializer)
|
||||
|
||||
// Create a mock method inliner which always returns true.
|
||||
val methodInliner = object : MethodInliner(false, true, true) {
|
||||
override fun shouldInline(clazz: Clazz?, method: Method?, codeAttribute: CodeAttribute?): Boolean = true
|
||||
}
|
||||
|
||||
"Then the interface method is inlined" {
|
||||
programClassPool.classesAccept(
|
||||
AllMethodVisitor(
|
||||
AllAttributeVisitor(
|
||||
methodInliner
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val lengthAfter = codeAttr.u4codeLength
|
||||
|
||||
lengthAfter shouldBeGreaterThan lengthBefore
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -283,7 +283,7 @@ class MethodInlinerTest : FreeSpec({
|
||||
}
|
||||
}
|
||||
|
||||
"Given a method calling another method in an interface" - {
|
||||
"Given a method calling another non-private method in an interface" - {
|
||||
val (programClassPool, _) = ClassPoolBuilder.fromSource(
|
||||
JavaSource(
|
||||
"Foo.java",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
- Fix "NoClassDefFoundError: Failed resolution of: Lorg/apache/logging/log4j/LogManager" when using GSON optimization or `-addconfigurationdebugging`. (#326)
|
||||
- Don't drop Record attribute for records with no components. (proguard-core#118)
|
||||
- Fix potential duplication class when name obfuscating Kotlin multi-file facades.
|
||||
- Do not inline interface methods to avoid compilation errors during output writing due to an interface method being made package visible.
|
||||
- Do not inline interface methods during optimization to avoid compilation errors during output writing due to an interface method being made package visible.
|
||||
|
||||
## Version 7.3.2
|
||||
|
||||
|
||||
Reference in New Issue
Block a user