KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > soot > dava > toolkits > base > AST > transformations > FinalFieldDefinition


1 /* Soot - a J*va Optimization Framework
2  * Copyright (C) 2006 Nomair A. Naeem (nomair.naeem@mail.mcgill.ca)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */

19
20 package soot.dava.toolkits.base.AST.transformations;
21
22 import soot.*;
23 import soot.dava.*;
24 import soot.jimple.*;
25 import soot.jimple.internal.*;
26 import soot.grimp.internal.*;
27 import soot.dava.internal.AST.*;
28 import soot.dava.internal.asg.*;
29 import soot.dava.internal.javaRep.*;
30 import soot.dava.toolkits.base.AST.analysis.*;
31 import soot.dava.toolkits.base.AST.traversals.*;
32 import soot.dava.toolkits.base.AST.structuredAnalysis.*;
33
34 import java.util.*;
35
36 /**
37  * Maintained by: Nomair A. Naeem
38  */

39
40 /**
41  * CHANGE LOG: 30th January 2006: Class was created to get rid of the field
42  * might not be initialized error that used to show up when recompiling
43  * decompiled code Will be throughly covered in "Programmer Friendly Code" Sable
44  * Tech Report (2006)
45  *
46  */

47
48 /*
49  * This class makes sure there is an initialization of all final variables
50  * (static or non static). If we cant guarantee initialization (may be
51  * initialized on multiple paths but not all) then we remove the final keyword
52  */

53 public class FinalFieldDefinition {// extends DepthFirstAdapter{
54
SootClass sootClass;
55
56     SootMethod sootMethod;
57
58     DavaBody davaBody;
59     
60     List cancelFinalModifier;
61
62     public FinalFieldDefinition(ASTMethodNode node) {
63         davaBody = node.getDavaBody();
64         sootMethod = davaBody.getMethod();
65         sootClass = sootMethod.getDeclaringClass();
66
67         String JavaDoc subSignature = sootMethod.getName();
68         if (!(subSignature.compareTo("<clinit>") == 0 || subSignature
69                 .compareTo("<init>") == 0)) {
70             // dont care about these since we want only static block and
71
// constructors
72
// System.out.println("\n\nName"+sootMethod.getName()+"
73
// SubSignature:"+sootMethod.getSubSignature());
74
return;
75         }
76
77         // create a list of interesting vars
78
ArrayList interesting = findFinalFields();
79         if (interesting.size() == 0) {
80             // no final fields of interest
81
return;
82         }
83
84         cancelFinalModifier = new ArrayList();
85         analyzeMethod(node, interesting);
86
87         Iterator it = cancelFinalModifier.iterator();
88         while (it.hasNext()) {
89             SootField field = (SootField) it.next();
90             field.setModifiers((soot.Modifier.FINAL ^ 0xFFFF)
91                     & field.getModifiers());
92         }
93     }
94
95     /*
96      * this method finds all the final fields in this class and assigns them to
97      * the finalFields list
98      *
99      * Note this stores a list of SootFields!!!
100      *
101      * Fields which are initialized in their declaration should not be added
102      */

103     public ArrayList findFinalFields() {
104
105         // first thing is to get a list of all final fields in the class
106
ArrayList interestingFinalFields = new ArrayList();
107
108         Iterator fieldIt = sootClass.getFields().iterator();
109         while (fieldIt.hasNext()) {
110             SootField tempField = (SootField) fieldIt.next();
111             if (tempField.isFinal()) {
112
113                 // if its static final and method is static add
114
if (tempField.isStatic()
115                         && sootMethod.getName().compareTo("<clinit>") == 0) {
116                     interestingFinalFields.add(tempField);
117                 }
118
119                 // if its non static and final and method is constructor add
120
if ((!tempField.isStatic())
121                         && sootMethod.getName().compareTo("<init>") == 0) {
122                     interestingFinalFields.add(tempField);
123                 }
124             }
125         }
126         return interestingFinalFields;
127     }
128
129     public void analyzeMethod(ASTMethodNode node, List varsOfInterest) {
130         MustMayInitialize must = new MustMayInitialize(node,
131                 MustMayInitialize.MUST);
132
133         Iterator it = varsOfInterest.iterator();
134         while (it.hasNext()) {
135             SootField interest = (SootField) it.next();
136
137             // check for constant value tags
138
Type fieldType = interest.getType();
139             if (fieldType instanceof DoubleType
140                     && interest.hasTag("DoubleConstantValueTag")) {
141                 continue;
142             } else if (fieldType instanceof FloatType
143                     && interest.hasTag("FloatConstantValueTag")) {
144                 continue;
145             } else if (fieldType instanceof LongType
146                     && interest.hasTag("LongConstantValueTag")) {
147                 continue;
148             } else if (fieldType instanceof CharType
149                     && interest.hasTag("IntegerConstantValueTag")) {
150                 continue;
151             } else if (fieldType instanceof BooleanType
152                     && interest.hasTag("IntegerConstantValueTag")) {
153                 continue;
154             } else if ((fieldType instanceof IntType
155                     || fieldType instanceof ByteType || fieldType instanceof ShortType)
156                     && interest.hasTag("IntegerConstantValueTag")) {
157                 continue;
158             } else if (interest.hasTag("StringConstantValueTag")) {
159                 continue;
160             }
161
162             if (must.isMustInitialized(interest)) {
163                 // was initialized on all paths couldnt ask for more
164
continue;
165             }
166
167             // System.out.println("SootField: "+interest+" not initialized.
168
// checking may analysis");
169
MustMayInitialize may = new MustMayInitialize(node,
170                     MustMayInitialize.MAY);
171             if (may.isMayInitialized(interest)) {
172                 // System.out.println("It is initialized on some path just not
173
// all paths\n");
174
List defs = must.getDefs(interest);
175                 if (defs == null)
176                     throw new RuntimeException JavaDoc("Sootfield: " + interest
177                             + " is mayInitialized but the defs is null");
178
179                 handleAssignOnSomePaths(node, interest, defs);
180             } else {
181                 // not initialized on any path., assign default
182
// System.out.println("Final field is not initialized on any
183
// path--------ASSIGN DEFAULT VALUE");
184
assignDefault(node, interest);
185             }
186         }
187     }
188
189     /*
190      * One gets to this method only if there was NO definition of a static final
191      * field in the static body At the same time no TAG with a constant value
192      * matched, so we know the static final was not initialized at declaration
193      * time If this happens: though it shouldnt unless u come from non-java
194      * compilers...insert default value initialization into the static
195      * method...right at the end to make things easy
196      */

197     public void assignDefault(ASTMethodNode node, SootField f) {
198
199         // create initialization stmt
200
AugmentedStmt defaultStmt = createDefaultStmt(f);
201
202         if (defaultStmt == null)
203             return;
204
205         List subBodies = (List) node.get_SubBodies();
206         if (subBodies.size() != 1)
207             throw new RuntimeException JavaDoc(
208                     "SubBodies size of method node not equal to 1");
209
210         List body = (List) subBodies.get(0);
211
212         // check if the bodys last node is an ASTStatementSequenceNode where we
213
// might be able to add
214

215         boolean done = false;
216         if (body.size() != 0) {
217             ASTNode lastNode = (ASTNode) body.get(body.size() - 1);
218             if (lastNode instanceof ASTStatementSequenceNode) {
219                 List stmts = ((ASTStatementSequenceNode) lastNode)
220                         .getStatements();
221                 if (stmts.size() != 0) {
222                     Stmt s = ((AugmentedStmt) stmts.get(0)).get_Stmt();
223                     if (!(s instanceof DVariableDeclarationStmt)) {
224                         // can add statement here
225
stmts.add(defaultStmt);
226
227                         ASTStatementSequenceNode newNode = new ASTStatementSequenceNode(
228                                 stmts);
229                         // replace this node with the original node
230

231                         body.remove(body.size() - 1);
232                         body.add(newNode);
233
234                         node.replaceBody(body);
235                         done = true;
236                     }
237                 }
238             }
239         }
240         if (!done) {
241             List newBody = new ArrayList();
242             newBody.add(defaultStmt);
243
244             ASTStatementSequenceNode newNode = new ASTStatementSequenceNode(
245                     newBody);
246             body.add(newNode);
247
248             node.replaceBody(body);
249         }
250
251     }
252
253     public AugmentedStmt createDefaultStmt(Object JavaDoc field) {
254
255         Value ref = null;
256         Type fieldType = null;
257         if (field instanceof SootField) {
258             // have to make a static field ref
259
SootFieldRef tempFieldRef = ((SootField) field).makeRef();
260
261             fieldType = ((SootField) field).getType();
262             if (((SootField) field).isStatic())
263                 ref = new DStaticFieldRef(tempFieldRef, true);
264             else
265                 ref = new DInstanceFieldRef(new JimpleLocal("this", fieldType),
266                         tempFieldRef, new HashSet());
267
268         } else if (field instanceof Local) {
269             ref = (Local) field;
270             fieldType = ((Local) field).getType();
271         }
272
273         GAssignStmt assignStmt = null;
274
275         if (fieldType instanceof RefType) {
276             assignStmt = new GAssignStmt(ref, NullConstant.v());
277         } else if (fieldType instanceof DoubleType) {
278             assignStmt = new GAssignStmt(ref, DoubleConstant.v(0));
279         } else if (fieldType instanceof FloatType) {
280             assignStmt = new GAssignStmt(ref, FloatConstant.v(0));
281         } else if (fieldType instanceof LongType) {
282             assignStmt = new GAssignStmt(ref, LongConstant.v(0));
283         } else if (fieldType instanceof IntType
284                 || fieldType instanceof ByteType
285                 || fieldType instanceof ShortType
286                 || fieldType instanceof CharType
287                 || fieldType instanceof BooleanType) {
288
289             assignStmt = new GAssignStmt(ref, DIntConstant.v(0, fieldType));
290         }
291
292         if (assignStmt != null) {
293             // System.out.println("AssignStmt is"+assignStmt);
294
AugmentedStmt as = new AugmentedStmt(assignStmt);
295             return as;
296         } else
297             return null;
298
299     }
300
301     /*
302      * A sootfield gets to this method if it was an interesting field i.e static
303      * final for clinit and only final but non static for init and there was
304      * atleast one place that this var was defined but it was not defined on all
305      * paths and hence the recompilation will result in an error
306      *
307      * try{ staticFinal = defined; } catch(Exception e){}
308      */

309
310     public void handleAssignOnSomePaths(ASTMethodNode node, SootField field,
311             List defs) {
312     
313         if (defs.size() != 1) {
314             // give up by removing "final" if there are more than one defs
315
cancelFinalModifier.add(field);
316         } else {
317             // if there is only one definition
318

319             // see if there is no use of def
320
AllVariableUses varUses = new AllVariableUses(node);
321             node.apply(varUses);
322
323             List allUses = varUses.getUsesForField(field);
324
325             if (allUses != null && allUses.size() != 0) {
326                 /*
327                  * if the number of uses is not 0 then we dont want to get into
328                  * trying to delay initialization just before assignment.
329                  * Easier to remove "final"
330                  */

331                 cancelFinalModifier.add(field);
332             }
333             else {
334                 /*
335                  * we have a final field with 1 def and 0 uses but is not initialized on all paths
336                  * we can try to delay initialization using an indirect approach
337                  * STMT0 TYPE DavaTemp_fieldName;
338                  * STMT1 DavaTemp_fieldname = DEFAULT
339                  * try{ try{
340                  * field = ... STMT2 DavaTemp_fieldname = ...
341                  * } X
342                  * catch(...){ }catch(..){
343                  * .... ....
344                  * } }
345                  * STMT3 field = Dava_tempVar
346                  *
347                  * Notice the following code will try to place the field assignment
348                  * as close to the original assignment as possible.
349                  *
350                  * TODO: However there might still be issues with delaying this assignment
351                  * e.g. what if the place marked by X (more specifically between
352                  * the original def and the new def includes a method invocation
353                  * which access the delayed field.
354                  *
355                  * Original Comment February 2nd, 2006: Laurie mentioned that apart from direct
356                  * uses we also have to be conservative about method calls since
357                  * we are dealing with fields here What if some method was
358                  * invoked and it tried to use a field whose initialization we
359                  * are about to delay. This can be done by implementing a small
360                  * analysis. (See end of this class file.
361                  *
362                  * TODO: SHOULD BE CHECKED FOR CODE BETWEEN THE OLD DEF AND THE NEW ASSIGNMENT
363                  * Currently checks from some point till end of method
364                  *
365                  * MethodCallFinder myMethodCallFinder = new MethodCallFinder( (GAssignStmt) defs.get(0));
366                  * node.apply(myMethodCallFinder);
367                  * if (myMethodCallFinder.anyMethodCalls()) {
368                  * // there was some method call after the definition stmt so
369                  * // we cant continue
370                  * // remove the final modifier and leave
371                  * //System.out.println("Method invoked somewhere after definition");
372                  * cancelFinalModifier.add(field);
373                  * return;
374                  * }
375                  */

376                 
377                 
378                 
379                 // Creating STMT0
380
Type localType = field.getType();
381                 Local newLocal = new JimpleLocal("DavaTemp_" + field.getName(),localType);
382
383                 DVariableDeclarationStmt varStmt = new DVariableDeclarationStmt(localType,davaBody);
384                 
385                 varStmt.addLocal(newLocal);
386                 AugmentedStmt as = new AugmentedStmt(varStmt);
387
388                 // System.out.println("Var Decl stmt"+as);
389

390                 // STORE IT IN Methods Declaration Node
391
ASTStatementSequenceNode declNode = node.getDeclarations();
392                 List stmts = declNode.getStatements();
393                 stmts.add(as);
394                 
395                 declNode = new ASTStatementSequenceNode(stmts);
396
397                 List subBodies = (List) node.get_SubBodies();
398                 if (subBodies.size() != 1)
399                     throw new DecompilationException("ASTMethodNode does not have one subBody");
400
401                 List body = (List) subBodies.get(0);
402
403                 body.remove(0);
404                 body.add(0, declNode);
405
406                 node.replaceBody(body);
407
408                 node.setDeclarations(declNode);
409
410                 
411                 // STMT1 initialization
412
AugmentedStmt initialization = createDefaultStmt(newLocal);
413                 /*
414                  * The first node in a method is the declarations
415                  * we know there is a second node because originaly the
416                  * field was initialized on some path
417                  */

418                 if (body.size() < 2)
419                     throw new RuntimeException JavaDoc("Size of body is less than 1");
420
421                 /*
422                  * If the second node is a stmt seq we put STMT1 there
423                  * otherwise we create a new stmt seq node
424                  */

425
426                 ASTNode nodeSecond = (ASTNode) body.get(1);
427                 if (nodeSecond instanceof ASTStatementSequenceNode) {
428                     // the second node is a stmt seq node just add the stmt here
429
List stmts1 = ((ASTStatementSequenceNode) nodeSecond).getStatements();
430                     stmts1.add(initialization);
431                     nodeSecond = new ASTStatementSequenceNode(stmts1);
432                     // System.out.println("Init added in exisiting node");
433
body.remove(1);
434                 } else {
435                     //System.out.println("had to add new node");
436
List tempList = new ArrayList();
437                     tempList.add(initialization);
438                     nodeSecond = new ASTStatementSequenceNode(tempList);
439                 }
440                 body.add(1, nodeSecond);
441                 node.replaceBody(body);
442
443                 
444                 
445                 //STMT2
446
//done by simply replacing the leftop in the original stmt
447
((GAssignStmt) defs.get(0)).setLeftOp(newLocal);
448
449                 
450                 
451                 //STMT3
452

453                 // have to make a field ref
454
SootFieldRef tempFieldRef = ((SootField) field).makeRef();
455
456                 Value ref;
457                 if (field.isStatic())
458                     ref = new DStaticFieldRef(tempFieldRef, true);
459                 else {
460                     ref = new DInstanceFieldRef(new JimpleLocal("this", field
461                             .getType()), tempFieldRef, new HashSet());
462                     // throw new RuntimeException("STOPPED");
463
}
464
465                 GAssignStmt assignStmt = new GAssignStmt(ref, newLocal);
466                 AugmentedStmt assignStmt1 = new AugmentedStmt(assignStmt);
467
468
469
470                 /*
471                  * 14th February 2006
472                  * Should add this statement to the first place in the code where
473                  * we will have a mustInitialize satisfied
474                  */

475
476                 //the def is at (GAssignStmt) defs.get(0)
477
//its parent is ASTStatementSequence and its parent is now needed
478
ASTParentNodeFinder parentFinder = new ASTParentNodeFinder();
479                 node.apply(parentFinder);
480                 
481                 Object JavaDoc parent = parentFinder.getParentOf(defs.get(0));
482                 if(!(parent instanceof ASTStatementSequenceNode)){
483                     throw new DecompilationException("Parent of stmt was not a stmt seq node");
484                 }
485                 
486                 Object JavaDoc grandParent = parentFinder.getParentOf(parent);
487                 if( !(parent instanceof ASTMethodNode) && grandParent == null){
488                     throw new DecompilationException("Parent of stmt seq node was null");
489                 }
490                 
491                 //so we have the parent stmt seq node and the grandparent node
492
//so it is the grandparent which is causing the error in MUSTINitialize
493
//we should move our assign right after the grandParent is done
494

495                 
496                 MustMayInitialize must = new MustMayInitialize(node,MustMayInitialize.MUST);
497                 while(!must.isMustInitialized(field)) {
498                     
499                     //System.out.println("not must initialized");
500
Object JavaDoc parentOfGrandParent = parentFinder.getParentOf(grandParent);
501                     if( !(grandParent instanceof ASTMethodNode) && parentOfGrandParent == null){
502                         throw new DecompilationException("Parent of non method node was null");
503                     }
504                     boolean notResolved=false;
505                     // look for grandParent in parentOfGrandParent
506
ASTNode ancestor = (ASTNode)parentOfGrandParent;
507                     List ancestorBodies = ancestor.get_SubBodies();
508                     Iterator it = ancestorBodies.iterator();
509                     while(it.hasNext()){
510                         List ancestorSubBody = null;
511                     
512                         if (ancestor instanceof ASTTryNode)
513                             ancestorSubBody = (List) ((ASTTryNode.container) it.next()).o;
514                         else
515                             ancestorSubBody = (List) it.next();
516                      
517                         if(ancestorSubBody.indexOf(grandParent) > -1) {
518                             //grandParent is present in this body
519
int index = ancestorSubBody.indexOf(grandParent);
520                             //check the next index
521

522                             if(index+1 < ancestorSubBody.size() && ancestorSubBody.get(index+1) instanceof ASTStatementSequenceNode ){
523                                 //there is an stmt seq node node after the grandParent
524
ASTStatementSequenceNode someNode = (ASTStatementSequenceNode)ancestorSubBody.get(index+1);
525                              
526                                 //add the assign stmt here
527

528                                 List stmtsLast = ((ASTStatementSequenceNode) someNode).getStatements();
529                                 List newStmts = new ArrayList();
530                                 newStmts.add(assignStmt1);
531                                 newStmts.addAll(stmtsLast);
532                                 someNode.setStatements(newStmts);
533                                 System.out.println("here1");
534                                 //check if problem is solved else remove the assign and change parents
535
must = new MustMayInitialize(node,MustMayInitialize.MUST);
536                                 if(!must.isMustInitialized(field)){
537                                     //problem not solved remove the stmt just added
538
someNode.setStatements(stmtsLast);
539                                     notResolved=true;
540                                 }
541                             }
542                             else{
543                                 //create a new stmt seq node and add it here
544
List tempList = new ArrayList();
545                                     tempList.add(assignStmt1);
546                                     ASTStatementSequenceNode lastNode = new ASTStatementSequenceNode(tempList);
547                                     ancestorSubBody.add(index+1,lastNode);
548                                     //node.replaceBody(body);
549
System.out.println("here2");
550                                     //check if problem is solved else remove the assign and change parents
551
must = new MustMayInitialize(node,MustMayInitialize.MUST);
552                                     if(!must.isMustInitialized(field)){
553                                         //problem not solved remove the stmt just added
554
ancestorSubBody.remove(index+1);
555                                         notResolved=true;
556                                     }
557                             }
558                             break;//break the loop going through subBodies
559
} //if ancestor was found
560

561                      } //next subBody
562
if(notResolved){
563                         //meaning we still dont have must initialization
564
//we should put assign in one level above than current
565
grandParent = parentFinder.getParentOf(grandParent);
566                         //System.out.println("Going one level up");
567
}
568                 }//while ! ismustinitialized
569
}
570         }
571     }
572 }
573
574
575
576 /*
577  * TODO: Change the analysis below to find method calls between GAssignStmt def
578  * and the new Assign Stmt.
579  */

580
581 class MethodCallFinder extends DepthFirstAdapter {
582
583     GAssignStmt def;
584
585     boolean foundIt = false;
586
587     boolean anyMethodCalls = false;
588
589     public MethodCallFinder(GAssignStmt def) {
590         this.def = def;
591     }
592
593     public MethodCallFinder(boolean verbose, GAssignStmt def) {
594         super(verbose);
595         this.def = def;
596     }
597
598     public void outDefinitionStmt(DefinitionStmt s) {
599         if (s instanceof GAssignStmt) {
600             if (((GAssignStmt) s).equals(def)) {
601                 foundIt = true;
602                 //System.out.println("Found it" + s);
603
}
604         }
605     }
606
607     public void inInvokeExpr(InvokeExpr ie) {
608         //System.out.println("In invoke Expr");
609
if (foundIt) {
610             //System.out.println("oops invoking something after definition");
611
anyMethodCalls = true;
612         }
613     }
614
615     /*
616      * Method will return false if there were no method calls made after the
617      * definition stmt
618      */

619     public boolean anyMethodCalls() {
620         return anyMethodCalls;
621         //return false;
622
}
623
624 }
Popular Tags