0

So I have a Method

public modifiers Foo foo(Bar bar){
    blah;
    blah;
    veryInterestingStmt;
    moreBlah();
    return XYZ;
}

I now want to split this method s.t. everything in its body is extracted into a separate method (programmatically).

I.e.

public modifiers Foo foo(Bar bar){
    return trulyFoo(bar);
}

public modifiers Foo trulyFoo(Bar bar){
    blah;
    blah;
    veryInterestingStmt;
    moreBlah();
    return XYZ;
}

How do I do that, though?

The naive

private void fracture(SootMethod sm) {

        SootClass sc = sm.getDeclaringClass();

        String auxMethodName = sm.getName() + FRACTURE_SUFFIX;

        Type auxReturnType = sm.getReturnType();
        List<Type>auxParamTypes = new LinkedList<>(sm.getParameterTypes());
        int auxModifiers = sm.getModifiers();

        SootMethod auxMethod = sc.addMethod(new SootMethod(auxMethodName,auxParamTypes,auxReturnType,auxModifiers));

        Body body = sm.getActiveBody();
        Body auxBody = Jimple.v().newBody(auxMethod);
        auxMethod.setActiveBody(auxBody);

        for(Local l : body.getLocals()){
            auxBody.getLocals().add(l);
        }

        PatchingChain<Unit> units = body.getUnits();
        PatchingChain<Unit> auxUnits = auxBody.getUnits();

        Iterator<Unit> it = body.getUnits().snapshotIterator();
        boolean passedFirstNonidentity = false;
        while(it.hasNext()){
            Stmt stmt = (Stmt) it.next();
            if(!passedFirstNonidentity && !(stmt instanceof IdentityStmt)) {
                passedFirstNonidentity = true;
                //TODO: if added more parameters than original method had, add their identity stmts here
            }

            auxUnits.add(stmt);
//            if(passedFirstNonidentity) units.remove(stmt); //TODO: uncomment this and later add call to {@code auxMethod}
        }
    }
}

Doesn't work. If I run, say

DirectedGraph dg = new ExceptionalUnitGraph(auxMethod.getActiveBody());

I get a

java.lang.RuntimeException: Unit graph contains jump to non-existing target
    at soot.toolkits.graph.UnitGraph.buildUnexceptionalEdges(UnitGraph.java:128)
    at soot.toolkits.graph.ExceptionalUnitGraph.initialize(ExceptionalUnitGraph.java:258)
    at soot.toolkits.graph.ExceptionalUnitGraph.<init>(ExceptionalUnitGraph.java:159)
    at soot.toolkits.graph.ExceptionalUnitGraph.<init>(ExceptionalUnitGraph.java:192)
User1291
  • 7,664
  • 8
  • 51
  • 108
  • 1
    Not an answer, but most reputable IDEs (e.g. IntelliJ or Eclipse) have tools which can extract a method in a safe way that won't break any code. – Tim Biegeleisen Mar 17 '18 at 12:50
  • @TimBiegeleisen except I don't have the method that I need to split available for the IDE. The method gets loaded, modified, (should get SPLIT), modified some more and *eventually* stored back to a ``.class`` file. – User1291 Mar 17 '18 at 12:57

2 Answers2

0

The technique of moving code without altering the behavior of the code is called Refactoring and is nicely covered in a book by Martin Fowler.

In your case, I would take the following multi-step approach:

  1. Stand up a "do nothing" function in the function you wish to split, just above the lines of code you wish to move.
  2. Move one or two of those lines of code from the surrounding function int the "do nothing" function, splitting the function, but having the split be a nested call.
  3. Move the split function up (or down) to the edge of the block in the surronding function.
  4. Move teh slpit function out of the block, placing new calls to it either prior to every call of the original function, or after every call of the original function. Note that you may have to rework the handling of return parameters, depending on the details.

It is strongly suggested that you write a set of tests to validate some, if not most, of the overall functionality of this block first. Then, after each change run your tests to verify that you didn't change behavior.

What you are seeing now is a change in behavior which came about by modifying the text of the code in such a manner that it did change behavior. The set of safe transformations of source code is likely smaller than you previously believed, or maybe you just made a simple error. However, the work you are attempting requires more knowledge than can be expressed in a StackOverflow style, question / answer, format. That's why I made the book reference.

If you can narrow the scope, you might get a better response in a future resubmission.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • Thanks for the recommendation. However, I do think this particular issue doesn't require anything too fancy. None of the statements were altered, no stmts were added and it still failed. If changing nothing changes functionality, then I should very much like to question the integrity of the framework and my sanity for using it. More likely seems to me that some information is lost in the transfer that would still be tied to the old method body, and since the body changed, is invalid. (Which soot for whatever reason doesn't report but instead ignores until something else fails.) – User1291 Mar 17 '18 at 14:51
  • @User1291 When moving code, one has to consider the class hierarchy, as well as the new locations of the moved code, and a number of small, yet important details. Otherwise you don't move the code without changing the behavior. If you change the behavior, odds are you will notice that change where you are looking for it, and not notice other changes until much later, making it more difficult to spot what went wrong (because you'll be in the middle of another task). – Edwin Buck Mar 17 '18 at 14:54
0

It seems that moving stmts just doesn't work. In contrast, completely replacing the body

        Body originalBody = sm.getActiveBody();
        originalBody.setMethod(auxMethod);
        auxMethod.setActiveBody(originalBody);
        Body newBody = Jimple.v().newBody(sm);
        sm.setActiveBody(newBody);

and then regenerating the locals, identity stmts (and other stmts you may need) in the newBody looks like a sensible way to go.

User1291
  • 7,664
  • 8
  • 51
  • 108