Skip to content

Commit

Permalink
fix polymorphic dispatch handling for ambiguous methods
Browse files Browse the repository at this point in the history
  • Loading branch information
mbenz89 committed Feb 27, 2019
1 parent 5d67dc8 commit af36e2a
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 29 deletions.
34 changes: 18 additions & 16 deletions src/main/java/soot/PolymorphicMethodRef.java
Expand Up @@ -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 {

Expand Down Expand Up @@ -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);
}
}
}

}
}

Expand Down
42 changes: 39 additions & 3 deletions src/systemTest/java/soot/jimple/PolymorphicDispatchTest.java
Expand Up @@ -25,6 +25,7 @@
import org.junit.Assert;
import org.junit.Test;

import soot.Body;
import soot.PackManager;
import soot.SootMethod;
import soot.Unit;
Expand All @@ -34,6 +35,7 @@

/**
* @author Andreas Dann created on 06.02.19
* @author Manuel Benz 27.2.19
*/
public class PolymorphicDispatchTest extends AbstractTestingFramework {

Expand All @@ -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
Expand All @@ -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) {
Expand Down
27 changes: 17 additions & 10 deletions src/systemTest/targets/soot/jimple/PolymorphicDispatch.java
Expand Up @@ -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);
}
}

0 comments on commit af36e2a

Please sign in to comment.