diff --git a/src/main/java/soot/PolymorphicMethodRef.java b/src/main/java/soot/PolymorphicMethodRef.java index 006dbf92e77..889c0341aa0 100644 --- a/src/main/java/soot/PolymorphicMethodRef.java +++ b/src/main/java/soot/PolymorphicMethodRef.java @@ -35,6 +35,7 @@ * special treatment * * @author Andreas Dann created on 06.02.19 + * @author Manuel Benz 27.2.19 */ public class PolymorphicMethodRef extends SootMethodRefImpl { @@ -89,25 +90,26 @@ public SootMethod resolve() { if (method != null) { return method; } - // no method with matching parametertypes or return types found - // for polymorphic methods, we don't care about the return or parameter types. We just check if a method with the name - // exists - - method = getDeclaringClass().getMethodByName(getName()); - if (method != null) { - // the class declares a method with that name, check if the method is polymorphic - Tag visibilityAnnotationTag = method.getTag("VisibilityAnnotationTag"); - if (visibilityAnnotationTag != null) { - for (AnnotationTag annotation : ((VisibilityAnnotationTag) visibilityAnnotationTag).getAnnotations()) { - // check the annotation's type - if (annotation.getType().equals("L" + POLYMORPHIC_SIGNATURE + ";")) { - // the method is polymorphic, add a fitting method to the MethodHandle or VarHandle class, as the JVM does on - // runtime - return addPolyMorphicMethod(method); + // no method with matching parameter types or return types found + // for polymorphic methods, we don't care about the return or parameter types. We just check if a method with the name + // exists and has a polymorphic type signature + + // Note(MB): We cannot use getMethodByName here since the method name is ambiguous after adding the first method with + // same name and refined signature + for (SootMethod candidateMethod : getDeclaringClass().getMethods()) { + if (candidateMethod.getName().equals(getName())) { + Tag annotationsTag = candidateMethod.getTag("VisibilityAnnotationTag"); + if (annotationsTag != null) { + for (AnnotationTag annotation : ((VisibilityAnnotationTag) annotationsTag).getAnnotations()) { + // check the annotation's type + if (annotation.getType().equals("L" + POLYMORPHIC_SIGNATURE + ";")) { + // the method is polymorphic, add a fitting method to the MethodHandle or VarHandle class, as the JVM does on + // runtime + return addPolyMorphicMethod(candidateMethod); + } } } - } } diff --git a/src/systemTest/java/soot/jimple/PolymorphicDispatchTest.java b/src/systemTest/java/soot/jimple/PolymorphicDispatchTest.java index f47749318fa..a20b1468d23 100644 --- a/src/systemTest/java/soot/jimple/PolymorphicDispatchTest.java +++ b/src/systemTest/java/soot/jimple/PolymorphicDispatchTest.java @@ -25,6 +25,7 @@ import org.junit.Assert; import org.junit.Test; +import soot.Body; import soot.PackManager; import soot.SootMethod; import soot.Unit; @@ -34,6 +35,7 @@ /** * @author Andreas Dann created on 06.02.19 + * @author Manuel Benz 27.2.19 */ public class PolymorphicDispatchTest extends AbstractTestingFramework { @@ -44,6 +46,9 @@ protected void setupSoot() { Options.v().set_allow_phantom_refs(false); Options.v().set_no_bodies_for_excluded(false); Options.v().set_prepend_classpath(true); + // if we use validate globally, every test will fail due to validation of target methods of other tests. Even if the test + // would actually pass... + Options.v().set_validate(false); } @Override @@ -53,11 +58,42 @@ protected void runSoot() { @Test public void findsTarget() { - String methodSignature = methodSigFromComponents(TEST_TARGET_CLASS, "void", "test", ""); + String methodSignature = methodSigFromComponents(TEST_TARGET_CLASS, "void", "unambiguousMethod", ""); final SootMethod sootMethod = prepareTarget(methodSignature, TEST_TARGET_CLASS); Assert.assertTrue(sootMethod.isConcrete()); - Assert.assertNotNull(sootMethod.retrieveActiveBody()); - for (Unit u : sootMethod.getActiveBody().getUnits()) { + + Body body = sootMethod.retrieveActiveBody(); + Assert.assertNotNull(body); + // validate individual method + body.validate(); + + for (Unit u : body.getUnits()) { + if (u instanceof AssignStmt) { + Value right = ((AssignStmt) u).getRightOp(); + if (right instanceof InvokeExpr) { + SootMethod m = ((InvokeExpr) right).getMethodRef().resolve(); + Assert.assertFalse(m.isPhantom()); + Assert.assertTrue(m.isDeclared()); + if (m.getName().equals("invoke")) { + Assert.assertTrue(m.isNative()); + } + } + } + } + } + + @Test + public void handlesAmbiguousMethod() { + String methodSignature = methodSigFromComponents(TEST_TARGET_CLASS, "void", "ambiguousMethod", ""); + final SootMethod sootMethod = prepareTarget(methodSignature, TEST_TARGET_CLASS); + Assert.assertTrue(sootMethod.isConcrete()); + + Body body = sootMethod.retrieveActiveBody(); + Assert.assertNotNull(body); + // validate individual method + body.validate(); + + for (Unit u : body.getUnits()) { if (u instanceof AssignStmt) { Value right = ((AssignStmt) u).getRightOp(); if (right instanceof InvokeExpr) { diff --git a/src/systemTest/targets/soot/jimple/PolymorphicDispatch.java b/src/systemTest/targets/soot/jimple/PolymorphicDispatch.java index df0ee437dec..38d2cb21a41 100644 --- a/src/systemTest/targets/soot/jimple/PolymorphicDispatch.java +++ b/src/systemTest/targets/soot/jimple/PolymorphicDispatch.java @@ -24,20 +24,27 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; - - /** * @author Andreas Dann created on 06.02.19 + * @author Manuel Benz 27.2.19 */ public class PolymorphicDispatch { - - public void test() throws Throwable { - - MethodHandle methodHandle = MethodHandles.lookup().findVirtual(PolymorphicDispatch.class, "someMethod", null); - Object ob = methodHandle.invoke(); - System.out.println(ob); - - } + public void unambiguousMethod() throws Throwable { + MethodHandle methodHandle = MethodHandles.lookup().findVirtual(PolymorphicDispatch.class, "someMethod", null); + Object ob = methodHandle.invoke(); + System.out.println(ob); + } + + public void ambiguousMethod() throws Throwable { + MethodHandle methodHandle = MethodHandles.lookup().findVirtual(PolymorphicDispatch.class, "someMethod", null); + // call on sig 1 + Object ob = methodHandle.invoke(); + System.out.println(ob); + + // call on sig 2 + int res = (int) methodHandle.invoke(1); + System.out.println(res); + } } \ No newline at end of file