User1291
User1291

Reputation: 8219

Moving all statements from one method to another

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)

Upvotes: 0

Views: 88

Answers (2)

User1291
User1291

Reputation: 8219

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.

Upvotes: 0

Edwin Buck
Edwin Buck

Reputation: 70949

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.

Upvotes: 0

Related Questions