diff --git a/base/src/main/java/proguard/evaluation/ExecutingInvocationUnit.java b/base/src/main/java/proguard/evaluation/ExecutingInvocationUnit.java index f3fe2d4b..e3024ac3 100644 --- a/base/src/main/java/proguard/evaluation/ExecutingInvocationUnit.java +++ b/base/src/main/java/proguard/evaluation/ExecutingInvocationUnit.java @@ -267,12 +267,18 @@ public boolean methodMayHaveSideEffects( @Override public Value getMethodReturnValue( Clazz clazz, AnyMethodrefConstant anyMethodrefConstant, String returnType) { - if (anyMethodrefConstant.referencedMethod == null || parameters == null) { + if (anyMethodrefConstant.referencedMethod == null) { + return super.getMethodReturnValue(clazz, anyMethodrefConstant, returnType); + } + + if (!isStatic && parameters == null) { + log.error("Parameters unexpectedly null for non-static method"); return super.getMethodReturnValue(clazz, anyMethodrefConstant, returnType); } MethodExecutionInfo methodInfo = - new MethodExecutionInfo(anyMethodrefConstant, null, parameters); + new MethodExecutionInfo( + anyMethodrefConstant, null, parameters == null ? new Value[0] : parameters); Executor executor = executorLookup.lookupExecutor(methodInfo); MethodResult result = executeMethod(executor, methodInfo); diff --git a/base/src/main/java/proguard/evaluation/executor/MethodExecutionInfo.java b/base/src/main/java/proguard/evaluation/executor/MethodExecutionInfo.java index 39859cd2..c30338f5 100644 --- a/base/src/main/java/proguard/evaluation/executor/MethodExecutionInfo.java +++ b/base/src/main/java/proguard/evaluation/executor/MethodExecutionInfo.java @@ -71,8 +71,13 @@ public class MethodExecutionInfo { * @param parameters The parameters of the call, calling instance included. */ public MethodExecutionInfo( - @NotNull Clazz clazz, Method method, CodeLocation caller, Value... parameters) { + @NotNull Clazz clazz, + @NotNull Method method, + @Nullable CodeLocation caller, + @NotNull Value... parameters) { Objects.requireNonNull(clazz); + Objects.requireNonNull(method); + Objects.requireNonNull(parameters); targetClass = clazz; signature = (MethodSignature) Signature.of(clazz, method); this.caller = caller; diff --git a/base/src/test/kotlin/proguard/analysis/ExecutingInvocationUnitTest.kt b/base/src/test/kotlin/proguard/analysis/ExecutingInvocationUnitTest.kt index b246c4e5..faa481ef 100644 --- a/base/src/test/kotlin/proguard/analysis/ExecutingInvocationUnitTest.kt +++ b/base/src/test/kotlin/proguard/analysis/ExecutingInvocationUnitTest.kt @@ -6,9 +6,15 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.types.shouldBeInstanceOf import io.kotest.matchers.types.shouldNotBeInstanceOf +import proguard.classfile.ClassConstants +import proguard.classfile.MethodSignature import proguard.evaluation.ExecutingInvocationUnit +import proguard.evaluation.MethodResult import proguard.evaluation.PartialEvaluator import proguard.evaluation.ParticularReferenceValueFactory +import proguard.evaluation.ValueCalculator +import proguard.evaluation.executor.Executor +import proguard.evaluation.executor.MethodExecutionInfo import proguard.evaluation.value.ArrayReferenceValueFactory import proguard.evaluation.value.DetailedArrayValueFactory import proguard.evaluation.value.IdentifiedReferenceValue @@ -377,4 +383,70 @@ class ExecutingInvocationUnitTest : FreeSpec({ val str = partialEvaluator.getVariablesBefore(instruction).getValue(variableTable["str"]!!) str.shouldNotBeInstanceOf() } + + "Static method with no parameters executed" - { + + val code = JavaSource( + "Test.java", + """ + class Test { + public void test(){ + String str = foo(); + } + + public static String foo() { + return "42"; + } + } + """, + ) + + val (programClassPool, libraryClassPool) = ClassPoolBuilder.fromSource(code, javacArguments = listOf("-g", "-source", "1.8", "-target", "1.8")) + val valueFactory: ValueFactory = ParticularValueFactory(DetailedArrayValueFactory(), ParticularReferenceValueFactory()) + + val fooExecutor = object : Executor { + + override fun getMethodResult( + methodData: MethodExecutionInfo, + valueCalculator: ValueCalculator, + ): MethodResult { + return MethodResult.Builder() + .setReturnValue( + valueCalculator.apply( + ClassConstants.TYPE_JAVA_LANG_STRING, + libraryClassPool.getClass(ClassConstants.NAME_JAVA_LANG_STRING), + true, + "42", + false, + null, + ), + ).build() + } + + override fun getSupportedMethodSignatures(): MutableSet { + return mutableSetOf(MethodSignature("Test", "foo", "()Ljava/lang/String;")) + } + } + + val invocationUnit = ExecutingInvocationUnit.Builder().addExecutor { fooExecutor }.build(valueFactory, libraryClassPool) + val partialEvaluator = PartialEvaluator( + valueFactory, + invocationUnit, + false, + ) + + val (instructions, variableTable) = PartialEvaluatorUtil.evaluate( + "Test", + "test", + "()V", + programClassPool, + partialEvaluator, + ) + + val (instruction, _) = instructions.last() + + val str = partialEvaluator.getVariablesBefore(instruction).getValue(variableTable["str"]!!) + str.shouldBeInstanceOf() + str.value.preciseValue shouldBe "42" + } }) diff --git a/docs/md/releasenotes.md b/docs/md/releasenotes.md index 84385d95..86710027 100644 --- a/docs/md/releasenotes.md +++ b/docs/md/releasenotes.md @@ -1,5 +1,8 @@ ## Version 9.1.5 +### Improved +- `ExecutingInvocationUnit` now supports execution of static methods with no parameters. + ### Bugfixes - Prevent `unknown enum value for KmVersionRequirementVersionKind` exception when processing code compiled with an outdated Kotlin version.