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)
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:
Stand up a "do nothing" function in the function you wish to split, just above the lines of code you wish to move.
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.
Move the split function up (or down) to the edge of the block in the surronding function.
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.
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.
Related
I love Optional in Java. It has, in one simple class, allowed me to clearly identify return types and arguments which may or may not be available.
One thing that I struggle with is the necessity of assigning it to a short-lived variable which is then inherited into every subsequent scope.
I like to use the simple variable name opt when using optionals like this:
Optional<ThingA> opt = maybeGetThing();
if (opt.isPresent()) {
ThingA usefulVariableName = opt.get();
...
But when I then need a variable name to use in this scope...
void method() {
Optional<ThingA> opt = maybeGetThing();
if (opt.isPresent()) {
ThingA usefulVariableName = opt.get();
usefulVariableName.doA();
usefulVariableName.doB();
usefulVariableName.doC();
// Duplicate local variable opt
Optional<ThingB> opt = usefulVariableName.maybeAnotherThing();
}
}
I can use things like optA and optB and so on. But I wonder if there is another way to write this code without having to enumerate my temporary variables. This just smacks of lazy variable names like a aaaa aaaaaabbb or something.
I don't want to name all of my optionals explicitly like this:
Optional<ThingA> optUsefulVariableName = maybeGetThing();
if (optUsefulVariableName.isPresent()) {
ThingA usefulVariableName = optUsefulVariableName.get();
...
While accurate, it is extremely verbose. I also try to use throwaway names like opt and i to indicate that these are in fact only temporary and should serve no purpose beyond their immediate scope (even though they will be inherited).
UPDATE:
I have seen suggestions for using ifPresent() but I don't see how I can use this for instances where I also need to perform an action if the optional is empty:
void method() {
Optional<ThingA> opt = maybeGetThing();
if (!opt.isPresent()) {
doSomethingOnlyHere();
return;
}
if (opt.isPresent()) {
ThingA usefulVariableName = opt.get();
usefulVariableName.doA();
usefulVariableName.doB();
usefulVariableName.doC();
// Duplicate local variable opt
Optional<ThingB> opt = usefulVariableName.maybeAnotherThing();
}
}
When I try to refactor with ifPresent():
void method() {
// Doesn't handle instance where I need side effects on an empty optional
maybeGetThing().ifPresent(usefulVariableName -> {
...
}
}
The most basic way to eliminate the variable and the need to call Optional#get is to use Optional.ifPresent which calls a function if the Optional has a value.
maybeGetThing().ifPresent(val -> {
// do stuff with side effects here
});
This is still quite a limited way to use Optional, as one of Optionals key purposes is to facilitate programming in a functional style. If you are a beginner this may be a little lost on you, but the idea is to have functions that return something and not functions that rely on side effects. Functions relying on side effects cannot be chained together and are generally harder to reason about.
Technically Optional is something called a Functor (from category theory). It is a wrapper around a value (Whatever T is) and it allows the value to be passed through a series of operations to operate on it and pass it to the next operation until we have what we want, then the chain of operations ends with a terminal (i.e. final) operation. The terminal operation may return the unwrapped value if it exists or it could throw or return some default value if it doesn't.
For Optional it will skip any subsequent operations if the value becomes not present.
There are common operations like map, filter, flatMap (ok that's a Monad operation) and other more java specific operations like Optional#orElse and Optional#orElseThrow.
To refactor your example code you could do this.
void method() {
return maybeGetThing().flatMap(val -> {
// eek side effects
val.doA();
val.doB();
val.doC();
return val.maybeAnotherThing();
});
}
flatMap is a way of converting an Optional of one type to an Optional of another type. If the return value weren't Optional you would use map.
You can see we have eliminated the need for names of return values in favour of naming the parameters of lambda functions. The lambda functions are scoped so you can reuse the names if that's what you want to.
I generally like to provide runnable code, so here is a contrived example of what I mean which is runnable.
import java.util.Optional;
class DummyClass {
private int val = 0;
public void doA(){ val += 1; }
public void doB(){ val += 2; }
public void doC(){ val += 3; }
public Optional<String> maybeAnotherThing(){
return Optional.of(Integer.toString(val));
}
}
public class UseOptional5 {
Optional<DummyClass> maybeGetThing(){
return Optional.of(new DummyClass());
}
String method() {
return maybeGetThing()
// you can put other operations here
.flatMap(val -> {
// eek side effects
val.doA();
val.doB();
val.doC();
return val.maybeAnotherThing();
})
// you can put other operations here too
.orElseThrow(() -> new IllegalArgumentException("fail!!"));
}
public static void main(String args[]) {
UseOptional5 x = new UseOptional5();
System.out.println(x.method());
}
}
Since Java 9 I’d do
void method() {
maybeGetThing().ifPresentOrElse(
usefulVariableName -> {
usefulVariableName.doA();
usefulVariableName.doB();
usefulVariableName.doC();
// No duplicate local variable opt
Optional<ThingB> opt = usefulVariableName.maybeAnotherThing();
},
this::doSomethingOnlyHere
);
}
My rule of thumb is you seldom need or want to use isPresent and/or get, they are low-level. For basic things ifPresent (with f) and ifPresetnOrElse are fine. Others are correct that map and flatMap are very useful too.
I can't just replace the entire method. I have to just inject a reassignment of a local var after the last time it normally gets set but before it gets used near the end of the method.
Here's some pseudocode
void test() {
/* stuff */
String thing = null;
if (case1) {
thing = "case1"
}
if (case2) {
thing = "case2"
}
if (case3) {
thing = "case3"
}
if (thing == null) {
thing = "default";
}
/* I want to insert this code below with ASM */
thing = "Injected by ASM";
/* stuff */
}
I could also just replace the default assignment when the code reaches that point where it checks if thing == null. But the byte code for the default assignment is a pretty long StringBuilder with lots of appends. There's a LDC that I can use to uniquely identify that but line I dunno how to replace the whole thing assignment for that line. I only know how to replace the LDC (which is not enough).
The idea is I want to ignore all the case1-3 so that thing is always what I tell ASM to set it as
But the /* stuff */ at the top and bottom of the method cannot be removed
While you could visit the instructions in the method to find the last place it is set, and then pass over the code again to inject the change, the simplest approach is to translate the code so that every time the variable is set, you set it to the value you want. This might not even need more code, just replace what is there.
There are two code code 1:
if(isApplicable() || isGood()) {
//something
}
private boolean isApplicable() {
}
private boolean isGood() {
}
Code 2:
boolean applicable = isApplicable();
boolean good = isGood();
if(applicable || good) {
//something
}
private boolean isApplicable() {
}
private boolean isGood() {
}
Which of the the approach is good java practice ?
To me code1 seams more clean and code 2 seams to have extra code. code2 can make remote debugging easy.
To generalise your question, you're asking about the two forms:
// local variable form
Foo foo = methodReturningFoo();
Bar bar = methodTakingFoo(foo);
// inlined form
Bar bar = methodTakingFoo(methodReturningFoo());
Most modern IDEs have a shortcut to refactor between these at a keystroke: "inline" and "extract local variable". The fact that both refactorings exist is an indicator that both are appropriate, in different circumstances.
Inlining to a single statement makes the code more compact and sometimes more readable. You can see everything that's happening without having to read up to find out where a variable was set.
Here's a good candidate for inlining:
String name = customer.getName();
String greeting = createGreeting(name);
// ... becomes ...
String greeting = createGreeting(customer.getName());
Extracting a local variable turns what may be a long statement into two (or more) shorter statements. It may also allow you to re-use a value rather than calculate it twice.
Here's an example where we just break a statement into smaller chunks.
String greeting = createGreeting(greetingFactory.get(customer.getTitle()), customer.getName());
// ... becomes ...
Title title = customer.getTitle();
String name = customer.getName();
String greeting = createGreeting(greetingFactory.get(title), name));
... here's an example where we reuse a calculated value.
// doing the work twice
CustomerCategory category = findCategory(totalOrderValues(
customer.getOrders(currentMonth)));
List<Promotion> eligiblePromotions = findEligiblePromotions(totalOrderValues(
customer.getOrders(currentMonth)));
// ... becomes ...
BigInteger totalOrderValues = totalOrderValues(
customer.getOrders(currentMonth))
CustomerCategory category = findCategory(totalOrderValues);
List<Promotion> eligiblePromotions = findEligiblePromotions(totalOrderValues);
Generally, prefer the inlined version, until you see that the line is too long and complicated. Then extract a local variable (or extract a method) to make it neater. If it makes sense to store a value to avoid repeating an expensive calculation, then do so.
I am implementing a ray tracer and am having trouble with basic Java references :/ I have been staring at this for a while and I can't see the problem...
IntersectResult ir = new IntersectResult();
root.intersect(ray, ir);
if(r.material!=null)
System.out.println(result.material.diffuse);
// Doesn't print at all!!
// in my Node Class...
#Override
public void intersect(Ray ray, IntersectResult result) {
IntersectResult i = new IntersectResult();
for (Intersectable child:children){
child.intersect(ray, i);
if (result.t>i.t)
result = new IntersectResult(i);
}
if(result.material!=null)
System.out.println(result.material.diffuse); // prints correctly!
}
Basically my question is why is result.material null after the intersect method call when the print statements within the method call show that it is not?
This is a classic case of how the "pass-by-value" approach works and it is not specific to the code in question.
With respect to the latter, the ir reference passed to the intersect() method is redirected by the statement
result = new IntersectResult(i);
So, whenever the above statement is executed, the material object created is not saved in the original material variable, but to a local one, which is lost after the intersect method returns.
If you want to propagate that change, make the intersect() method always return the result object at its end and change the statement at the start of the code to
ir = root.intersect(ray, ir);
and correct the typo in the following if statement (it is ir.material, not r.material).
I'm sure there must be a standard way to do this, but my attempts to search Stackoverflow have failed.
I have a method like:
public void processSomeWidgetsForUser(int userItemId) {
Iterator<Widgets> iter = allWidgets.values().iterator();
while(iter.hasNext()) {
Widget thisWidget = iter.next();
if (userItemId == -1 || thisWidget.getUsersItemId() == userItemId) {
widget.process();
}
}
}
As you can see -1 is a "special value" meaning process all. Doing this saves repeating the loop code in another method called processSomeWidgetsForAllUsers.
But I dislike special values like this because they are easy to misuse or misunderstand, which is exactly the situation what I'm having to fix now (where someone thought -1 meant something else).
I can only think of two ways to improve this.
have a constant, containing -1 called something like
Widget.ALLWIDGETS which at least is self-documenting, but doesn't
stop code from using a -1 (if someone integrates old code in, for
example)
change the method to take a list of all user ids to
process, which can be empty, but that doesn't seem great
performance-wise (would need to retrieve all user ids first and then loop through
removing. Also what happens if the number of widgets in the list changes between
retreiving the ids and removing
Is there a better way? I'm sure I'm missing something obvious.
The above code has been changed slightly, so may not compile, but you should get the gist.
Although somewhat redundant, a fairly neat self-documenting approach could be to have 3 methods rather than one;
Make your original method private, and make one small change which would be to add your static final int EXECUTE_ALL = -1 and use that in your original method, then add the two new methods;
public void processWidget(int wID) throws IllegalArgumentException {
if(wID == EXECUTE_ALL) throw new IllegalArgumentException();
originalMethod(wID);
}
public void processAllWidgets() {
originalMethod(EXECUTE_ALL);
}
It makes your class a little more cluttered, but as far as the exposed methods go, it is clearer and hopefully foolproof. You could alter it not to throw an exception and just ignore any invalid ids, that just depends on your situation.
This approach of course has the major downside that it changes how the class appears to other classes, breaking everything that currently uses the, now private, originalMethod().
Number 1 would work very nicely. Be sure to document what the variable is though, so future coders (possibly yourself) know what it means.
/**This is the explanation for the below variable*/
public final static int ALL_WIDGETS = -1;
Have an external method like so:
static boolean idRepresentsAll(int id) {
return id == -1;
}
In this case, if you decide to replace it with a different mechanism, you only replace your magic number one place in your code.
At the very least, you would want to do something like this:
public static final int ID_REPRESENTING_ALL = -1;
You can change the method signature to accept a boolean for when you want to process them all.
public void processSomeWidgets(boolean doAll, int userItemId) {
Iterator<Widgets> iter = allWidgets.values().iterator();
while(iter.hasNext()) {
Widget thisWidget = iter.next();
if (doAll || thisWidget.getUsersItemId() == userItemId) {
widget.process();
}
}
}
This makes it more explicit, and easier to read in my opinion as there are no special values.