From 8bb7cc0c4be31e7b5e3df9c76850b44863953def Mon Sep 17 00:00:00 2001 From: Thomas Vochten Date: Wed, 20 Sep 2023 11:28:19 +0200 Subject: [PATCH] Do not inline methods from interfaces --- .../optimize/peephole/MethodInliner.java | 8 +++ .../optimize/peephole/MethodInlinerTest.kt | 54 +++++++++++++++++++ docs/md/manual/releasenotes.md | 1 + 3 files changed, 63 insertions(+) diff --git a/base/src/main/java/proguard/optimize/peephole/MethodInliner.java b/base/src/main/java/proguard/optimize/peephole/MethodInliner.java index 7ca66d5e..55d5fbf2 100644 --- a/base/src/main/java/proguard/optimize/peephole/MethodInliner.java +++ b/base/src/main/java/proguard/optimize/peephole/MethodInliner.java @@ -652,6 +652,14 @@ implements AttributeVisitor, AccessConstants.STATIC | AccessConstants.FINAL)) != 0 && + 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 && + DEBUG("Synchronized?") && // Only inline the method if it is not synchronized, etc. diff --git a/base/src/test/kotlin/proguard/optimize/peephole/MethodInlinerTest.kt b/base/src/test/kotlin/proguard/optimize/peephole/MethodInlinerTest.kt index 93ef182f..d6ef6030 100644 --- a/base/src/test/kotlin/proguard/optimize/peephole/MethodInlinerTest.kt +++ b/base/src/test/kotlin/proguard/optimize/peephole/MethodInlinerTest.kt @@ -282,6 +282,60 @@ class MethodInlinerTest : FreeSpec({ } } } + + "Given a method calling another method in an interface" - { + val (programClassPool, _) = ClassPoolBuilder.fromSource( + JavaSource( + "Foo.java", + """interface Foo { + default void f1() { + f2(); + } + + 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()[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 not inlined" { + programClassPool.classesAccept( + AllMethodVisitor( + AllAttributeVisitor( + methodInliner + ) + ) + ) + + val lengthAfter = codeAttr.u4codeLength + + lengthAfter shouldBeExactly lengthBefore + } + } }) private fun printProgramMethodInstructions( diff --git a/docs/md/manual/releasenotes.md b/docs/md/manual/releasenotes.md index 773f0d16..a9731a4e 100644 --- a/docs/md/manual/releasenotes.md +++ b/docs/md/manual/releasenotes.md @@ -13,6 +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. ## Version 7.3.2