如何通过编程将Java方法的全部语句提取到新方法(基于Soot)
如何通过编程方式提取方法体到新方法(基于Soot框架)
看起来你想要用Soot来实现提取方法这个经典重构操作——把原方法的业务逻辑迁移到一个新方法中,原方法只保留对新方法的调用。这完全可以通过Soot的API实现,我来帮你完善你写的fracture方法:
完整实现代码与步骤解析
步骤1:获取原方法的核心元数据
先从传入的SootMethod里提取我们需要的所有信息:所属类、修饰符、返回类型、参数列表,还有原方法的方法体。
private void fracture(SootMethod sm) { // 获取原方法所属的类 SootClass declaringClass = sm.getDeclaringClass(); // 跳过抽象方法或无方法体的方法 if (!sm.hasActiveBody()) { return; } Body originalBody = sm.getActiveBody(); // 提取原方法的关键元数据 int modifiers = sm.getModifiers(); Type returnType = sm.getReturnType(); List<Type> paramTypes = new ArrayList<>(); List<Local> originalParams = originalBody.getParameterLocals(); for (Local param : originalParams) { paramTypes.add(param.getType()); }
步骤2:创建新方法trulyFoo并迁移原方法逻辑
用原方法的元数据创建新方法,然后把原方法的局部变量、语句全部复制过去,同时修正参数引用(避免指向原方法的参数)。
// 1. 创建新方法签名 SootMethod newMethod = new SootMethod("trulyFoo", paramTypes, returnType); newMethod.setModifiers(modifiers); declaringClass.addMethod(newMethod); // 2. 为新方法构建方法体 Body newBody = Jimple.v().newBody(newMethod); newMethod.setActiveBody(newBody); List<Local> newParams = newBody.getParameterLocals(); // 3. 复制原方法的非参数局部变量 for (Local local : originalBody.getLocals()) { if (!originalParams.contains(local)) { newBody.getLocals().add(local.clone()); } } // 4. 复制原方法的所有语句 newBody.getUnits().addAll(originalBody.getUnits()); // 5. 修正参数引用:把原方法的参数替换为新方法的参数 Map<Local, Local> paramMapping = new HashMap<>(); for (int i = 0; i < originalParams.size(); i++) { paramMapping.put(originalParams.get(i), newParams.get(i)); } // 遍历所有语句替换参数引用 for (Unit unit : newBody.getUnits()) { unit.apply(new AbstractStmtSwitch() { @Override public void caseAssignStmt(AssignStmt stmt) { stmt.setLeftOp(swapLocal(stmt.getLeftOp(), paramMapping)); stmt.setRightOp(swapLocal(stmt.getRightOp(), paramMapping)); } @Override public void caseInvokeStmt(InvokeStmt stmt) { stmt.setInvokeExpr(swapLocalInInvoke(stmt.getInvokeExpr(), paramMapping)); } // 按需实现其他语句类型的替换,比如IfStmt、ReturnStmt等 }); }
步骤3:改造原方法的方法体
清空原方法的所有内容,替换为调用新方法并返回结果的逻辑。
// 清空原方法的旧内容 originalBody.getUnits().clear(); originalBody.getLocals().clear(); // 构建调用新方法的表达式 InvokeExpr invokeExpr; if (Modifier.isStatic(modifiers)) { invokeExpr = Jimple.v().newStaticInvokeExpr(newMethod.makeRef(), originalParams); } else { invokeExpr = Jimple.v().newVirtualInvokeExpr(originalBody.getThisLocal(), newMethod.makeRef(), originalParams); } // 根据返回类型生成对应的语句 if (!returnType.equals(VoidType.v())) { // 非void返回类型:调用新方法 -> 赋值给局部变量 -> 返回 Local resultLocal = Jimple.v().newLocal("result", returnType); originalBody.getLocals().add(resultLocal); originalBody.getUnits().add(Jimple.v().newAssignStmt(resultLocal, invokeExpr)); originalBody.getUnits().add(Jimple.v().newReturnStmt(resultLocal)); } else { // void返回类型:直接调用新方法 -> 返回 originalBody.getUnits().add(Jimple.v().newInvokeStmt(invokeExpr)); originalBody.getUnits().add(Jimple.v().newReturnVoidStmt()); } }
辅助方法:替换表达式中的局部变量
需要实现两个辅助方法来处理局部变量的替换:
private Value swapLocal(Value value, Map<Local, Local> mapping) { if (value instanceof Local && mapping.containsKey(value)) { return mapping.get(value); } // 处理嵌套表达式,比如ArrayRef、InstanceFieldRef等 if (value instanceof AbstractInstanceRef) { AbstractInstanceRef ref = (AbstractInstanceRef) value; ref.setBase(swapLocal(ref.getBase(), mapping)); } return value; } private InvokeExpr swapLocalInInvoke(InvokeExpr expr, Map<Local, Local> mapping) { List<Value> newArgs = new ArrayList<>(); for (Value arg : expr.getArgs()) { newArgs.add(swapLocal(arg, mapping)); } // 根据原InvokeExpr类型创建新的表达式 if (expr instanceof StaticInvokeExpr) { return Jimple.v().newStaticInvokeExpr(expr.getMethodRef(), newArgs); } else if (expr instanceof VirtualInvokeExpr) { VirtualInvokeExpr vExpr = (VirtualInvokeExpr) expr; return Jimple.v().newVirtualInvokeExpr(swapLocal(vExpr.getBase(), mapping), vExpr.getMethodRef(), newArgs); } return expr; }
关键注意事项
- 参数与局部变量的引用一致性:必须替换新方法中对原方法参数的引用,否则会出现"变量未定义"的错误。
- 访问权限同步:新方法的修饰符要和原方法保持一致(比如原方法是
private,新方法也设为private),确保类内可访问。 - 异常声明同步:如果原方法有
throws声明,记得给新方法也添加对应的异常列表。 - 合法性验证:操作完成后可以用
BodyValidator检查方法体的合法性,避免生成无效的Jimple代码。
内容的提问来源于stack exchange,提问作者User1291
相关产品推荐
相关产品推荐

