【问题标题】:Moving all statements from one method to another将所有语句从一种方法移动到另一种方法
【发布时间】:2018-03-17 12:48:10
【问题描述】:

所以我有一个方法

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

我现在想将此方法拆分为 s.t.其主体中的所有内容都被提取到一个单独的方法中(以编程方式)。

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

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

但是,我该怎么做呢?

天真

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}
        }
    }
}

不起作用。如果我跑,说

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

我得到一个

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)

【问题讨论】:

  • 不是一个答案,但大多数有信誉的 IDE(例如 IntelliJ 或 Eclipse)都有一些工具,可以以一种不会破坏任何代码的安全方式提取方法。
  • @TimBiegeleisen 除非我没有需要为 IDE 拆分的方法。该方法被加载、修改、(应该得到 SPLIT)、更多修改并最终存储回 .class 文件。

标签: java methods bytecode instrumentation soot


【解决方案1】:

在不改变代码行为的情况下移动代码的技术称为Refactoring,Martin Fowler 的一本书对此进行了很好的介绍。

在你的情况下,我会采取以下多步骤方法:

  1. 在您希望拆分的函数中,在您希望移动的代码行上方建立一个“什么都不做”函数。
  2. 将其中一两行代码从周围函数中移到“什么都不做”函数中,拆分函数,但拆分是嵌套调用。
  3. 将拆分函数向上(或向下)移动到环绕函数中块的边缘。
  4. 将 slpit 函数移出块,在每次调用原始函数之前或每次调用原始函数之后对其进行新调用。请注意,您可能需要重新处理返回参数的处理,具体取决于详细信息。

强烈建议您首先编写一组测试来验证此块的部分(如果不是大部分)整体功能。然后,在每次更改后运行测试以验证您没有更改行为。

您现在看到的是行为的改变,它是通过修改代码的文本来改变行为的。源代码的安全转换集可能比您之前认为的要小,或者您可能只是犯了一个简单的错误。但是,您正在尝试的工作需要比 StackOverflow 样式、问题/答案、格式表达的更多的知识。这就是我做这本书参考的原因。

如果你能缩小范围,你可能会在以后的重新提交中得到更好的回应。

【讨论】:

  • 感谢您的推荐。但是,我确实认为这个特定问题不需要太花哨的东西。没有更改任何语句,没有添加 stmts 并且仍然失败。如果没有任何改变会改变功能,那么我非常想质疑框架的完整性以及我使用它的理智。在我看来,更有可能是某些信息在传输中丢失了,这些信息仍然与旧方法主体相关联,并且由于主体已更改,因此无效。 (无论出于何种原因,烟灰都不会报告,而是会忽略,直到其他事情失败为止。)
  • @User1291 移动代码时,必须考虑类层次结构,以及移动代码的新位置,以及一些小而重要的细节。否则,您不会在不改变行为的情况下移动代码。如果你改变行为,你很可能会注意到你正在寻找的地方发生了变化,直到很久以后才注意到其他变化,这使得发现问题变得更加困难(因为你将处于另一项任务的中间)。
【解决方案2】:

似乎移动 stmts 是行不通的。相比之下,完全更换机身

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

然后在 newBody 中重新生成本地人、身份 stmts(以及您可能需要的其他 stmts)看起来是一个明智的选择。

【讨论】:

    猜你喜欢
    • 2017-03-19
    • 1970-01-01
    • 1970-01-01
    • 2013-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    • 1970-01-01
    相关资源
    最近更新 更多