View Javadoc

1   package joeq.Compiler.Analysis.FlowInsensitive;
2   
3   import java.io.FileReader;
4   import java.io.IOException;
5   import java.io.LineNumberReader;
6   import java.util.Collection;
7   import java.util.Iterator;
8   import java.util.LinkedList;
9   import java.util.StringTokenizer;
10  import joeq.Class.PrimordialClassLoader;
11  import joeq.Class.jq_Class;
12  import joeq.Class.jq_Method;
13  import joeq.Class.jq_Type;
14  import joeq.Compiler.Analysis.IPA.PA;
15  import joeq.Compiler.Analysis.IPA.ProgramLocation;
16  import joeq.Compiler.Analysis.IPA.ProgramLocation.QuadProgramLocation;
17  import joeq.Compiler.Quad.CodeCache;
18  import joeq.Main.HostedVM;
19  import joeq.UTF.Utf8;
20  import jwutil.util.Assert;
21  
22  /***
23   * @author V.Benjamin Livshits
24   * @version $Id: ReflectionInformationProvider.java 2465 2006-06-07 23:03:17Z joewhaley $
25   * 
26   * This class declares methods for resolving reflective calls.
27   */
28  public abstract class ReflectionInformationProvider {
29      public class NewInstanceTargets {
30          private jq_Method declaredIn;
31          private Collection targets = new LinkedList();
32  
33          /***
34           * @param declaredIn
35           */
36          public NewInstanceTargets(String declaredIn) {
37              this.declaredIn = getMethod(declaredIn);
38              if(PA.TRACE_REFLECTION){
39                  if(this.declaredIn == null) {
40                      System.out.println("No method for " + declaredIn + " in NewInstanceTargets. "
41                          + " The classpath is [" + PrimordialClassLoader.loader.classpathToString() + "]");
42                  } else {
43                      System.out.println("Created a NewInstanceTarget object for " + this.declaredIn.toString());
44                  }
45              }
46          }
47          
48          public boolean isValid(){
49              return getDeclaredIn() != null && targets.size() > 0;
50          }
51  
52          private jq_Method getMethod(String fullMethodName) {
53              int index = fullMethodName.lastIndexOf('.');
54              Assert._assert(index != -1);
55              
56              String className = fullMethodName.substring(0, index);
57              String methodName = fullMethodName.substring(index+1, fullMethodName.length());
58              
59              String classdesc = "L" + className.replace('.', '/') + ";";
60              jq_Class clazz = (jq_Class)PrimordialClassLoader.loader.getOrCreateBSType(classdesc);
61              
62  //            jq_Class clazz = (jq_Class) jq_Type.parseType(className);
63              try {
64                  clazz.prepare();
65              } catch (NoClassDefFoundError e){
66                  // no class found
67                  if(PA.TRACE_REFLECTION) System.err.println("Failed to load class " + className);
68                  return null;
69              }
70              jq_Method method = clazz.getDeclaredMethod(methodName);
71              Assert._assert(method != null);
72              
73              if(PA.TRACE_REFLECTION) System.out.println("Retrieved method " + method);
74              
75              return method;
76          }
77          
78          private jq_Class getClass(String className) {            
79              jq_Class clazz = (jq_Class) jq_Type.parseType(className);
80              try {
81                  clazz.prepare();
82              } catch (NoClassDefFoundError e){
83                  return null;
84              }
85              Assert._assert(clazz != null);
86              
87              return clazz;
88          }
89  
90          public void addTarget(String target) {
91              jq_Class clazz = getClass(target);
92              if(clazz != null){
93                  addTarget(clazz);
94              }
95          }
96  
97          public void addTarget(jq_Class clazz) {
98              jq_Method constructor = clazz.getInitializer(Utf8.get("()V"));
99              targets.add(constructor);
100         }
101         
102         public String toString(){
103             return declaredIn + " -> " + targets.toString();            
104         }
105 
106         public jq_Method getDeclaredIn() {
107             return this.declaredIn;
108         }
109 
110         public Collection getTargets() {
111             return this.targets;
112         }
113         
114         public void addSubclasses(String className) {
115             jq_Class clazz = getClass(className);
116             //Assert._assert(clazz != null);
117             for(Iterator iter = PrimordialClassLoader.loader.listPackages(); iter.hasNext();){
118                 //System.out.println("\t" + iter.next());
119                 String packageName = (String) iter.next();
120                 
121                 for(Iterator classIter = PrimordialClassLoader.loader.listPackage(packageName, true); classIter.hasNext();){
122                     String className2 = (String) classIter.next();
123                     className2 = className2.substring(0, className2.length()-6); 
124                     //System.out.println("\tClass: " + className2);
125                     jq_Class c = (jq_Class) jq_Type.parseType(className2);
126                     try {
127                         if(c.isSubtypeOf(clazz)){
128                             c.prepare();
129                             System.out.println(
130                                 "Initialized a subclass of " + className + 
131                                 ", class: " + c);    
132                         }                        
133                     } catch(Throwable e){
134                         continue;
135                     }
136                     
137                     
138                 }
139                 
140             }
141         }        
142     }
143     
144     /*** 
145      * Reflective methods to be used in isReflective(...)
146      */
147     private static String[][] methodSpecs = { 
148                             {"java.lang.Class", "forName"},
149                             {"java.lang.Object", "newInstance"},
150                             {"java.lang.reflection.Constructor", "newInstance"},
151                             };
152     
153     /***
154      * Checks if method is reflective.
155      * 
156      * @param method
157      */
158     public static boolean isReflective(jq_Method method){
159         for(int i = 0; i < methodSpecs.length; i++){
160             String[] methodSpec = methodSpecs[i];
161             String className = methodSpec[0];
162             String methodName = methodSpec[1];
163             
164             if(!className.equals(method.getDeclaringClass().getName())) continue;
165             if(!methodName.toString().equals(method.getName())) continue;
166             
167             return true;
168         }
169         
170         return false;
171     }
172 
173     /***
174      * Checks if mc corresponds to a newInstance call.
175      */
176     public static boolean isNewInstance(ProgramLocation.QuadProgramLocation mc){
177         jq_Method target = mc.getTargetMethod();
178         return isNewInstance(target);
179     }
180     
181     /***
182      * Checks if target is a newInstance method. 
183      */
184     public static boolean isNewInstance(jq_Method target) {
185         String className = target.getDeclaringClass().getName(); 
186         String methodName = target.getName().toString();
187         
188         if(!className.equals("java.lang.Class")) return false;
189         if(!methodName.equals("newInstance")) return false;
190         
191         return true;
192     }
193     
194     public static boolean isForName(jq_Method target) {
195         String className = target.getDeclaringClass().getName(); 
196         String methodName = target.getName().toString();
197         
198         if(!className.equals("java.lang.Class")) return false;
199         if(!methodName.equals("forName")) return false;
200         
201         return true;
202     }
203 
204     /***
205      * Resolves constructors being pointed to by a newInstance() call mc.
206      * */
207     public abstract Collection/*<jq_Method>*/  getNewInstanceTargets(ProgramLocation.QuadProgramLocation mc);
208     
209     /***
210      * Resolves constructors being pointed to by a newInstance() calls within 
211      * method n.
212      * 
213      * Notice that information may be imprecise because we only have one piece of 
214      * data per method.
215      * */
216     public abstract Collection/*<jq_Method>*/  getNewInstanceTargets(jq_Method n);
217     
218     /***
219      * This implementation of ReflectionInformationProvider 
220      * reads answers from a file. 
221      * */
222     public static class CribSheetReflectionInformationProvider extends ReflectionInformationProvider {
223         private static final String DEFAULT_CRIB_FILE = "reflection.spec";
224 
225         public CribSheetReflectionInformationProvider(String cribSheetFileName){
226             try {
227                 readSpec(cribSheetFileName);
228             } catch (IOException e) {
229                 // TODO Auto-generated catch block
230                 System.err.println("Error reading " + cribSheetFileName + e.getMessage());                
231             }
232         }
233               
234         public CribSheetReflectionInformationProvider() {
235             this(DEFAULT_CRIB_FILE);
236         }
237 
238         public static void main(String[] args) {
239             HostedVM.initialize();
240             CodeCache.AlwaysMap = true;
241             
242             CribSheetReflectionInformationProvider provider = 
243                 new CribSheetReflectionInformationProvider(args[0]);
244         }
245         
246         /***
247          * @param cribSheetFileName
248          * @throws IOException
249          */
250         private void readSpec_old(String cribSheetFileName) throws IOException {
251             FileReader fileIn = new FileReader(cribSheetFileName);
252             LineNumberReader in = new LineNumberReader(fileIn);
253             String line = in.readLine();
254             do {
255                 if(!line.startsWith("#") && line.trim().length() > 0){
256                     NewInstanceTargets spec = parseSpecLine(line);
257                     if(spec.isValid()){
258                         if(PA.TRACE_REFLECTION){
259                             System.out.println("Adding a reflection spec for " + spec.getDeclaredIn());
260                         }
261                         specs.add(spec);
262                     }
263                 }
264                 line = in.readLine();
265             } while (line != null);
266             in.close();
267             
268             if(PA.TRACE_REFLECTION) {
269                 System.out.println(
270                     "There are " + specs.size() +            
271                     " specifications read from " + cribSheetFileName);
272             }
273         }
274         
275         private void readSpec(String cribSheetFileName) throws IOException {
276             FileReader fileIn = new FileReader(cribSheetFileName);
277             LineNumberReader in = new LineNumberReader(fileIn);
278             String line = in.readLine();
279             NewInstanceTargets spec = null;
280             do {
281                 if(!line.startsWith("#") && line.trim().length() > 0){                    
282                     if(!Character.isWhitespace(line.charAt(0))){
283                         int indexBracket = line.indexOf('(');
284                         Assert._assert(indexBracket != -1, "No brackets in " + line);
285                         String declaredIn = line.substring(0, indexBracket);
286 
287                         if(spec != null){
288                             if(PA.TRACE_REFLECTION && spec.isValid()){
289                                 System.out.println("Read " + spec);
290                             }
291                             if(spec.isValid()){
292                                 if(PA.TRACE_REFLECTION){
293                                     System.out.println(
294                                         "Adding a reflection spec for " + spec.getDeclaredIn());
295                                 }
296                                 specs.add(spec);
297                             }
298                         }
299                         spec = new NewInstanceTargets(declaredIn);                        
300                     }else{
301                         line = line.trim();
302                         spec.addTarget(line);
303                     }
304                 }
305                 line = in.readLine();
306             } while (line != null);
307             in.close();
308             
309             if(PA.TRACE_REFLECTION) {
310                 System.out.println(
311                     "There are " + specs.size() +            
312                     " specifications read from " + cribSheetFileName);
313             }
314         }
315         
316         Collection/*<NewInstanceTargets>*/ specs      = new LinkedList();
317         private static final Object SUBCLASSES_MARKER = "<";
318         private static final Object ELLIPSES          = "...";
319 
320         /***
321          * Parses one line like this:
322              org.roller.presentation.RollerContext.getAuthenticator org.roller.presentation.DefaultAuthenticator ...
323         */
324         private NewInstanceTargets parseSpecLine(String line) {
325             StringTokenizer tok = new StringTokenizer(line);
326             String declaredIn = tok.nextToken();
327             NewInstanceTargets targets = new NewInstanceTargets(declaredIn);
328             while(tok.hasMoreTokens()){
329                 String token = tok.nextToken();
330                 if(!token.equals(ELLIPSES)){
331                     if(!token.equals(SUBCLASSES_MARKER)){
332                         targets.addTarget(token);
333                     }else{
334                         targets.addSubclasses(tok.nextToken());
335                     }
336                 }else{
337                     if(PA.TRACE_REFLECTION){
338                         System.err.println("Specification for " + declaredIn + " is incomplete.");
339                     }
340                 }
341             }
342             if(PA.TRACE_REFLECTION && targets.isValid()){
343                 System.out.println("Read " + targets);
344             }
345             
346             return targets;
347         }
348         
349         /* (non-Javadoc)
350          * @see joeq.Compiler.Analysis.FlowInsensitive.ReflectionInformationProvider#getNewInstanceTargets(joeq.Compiler.Analysis.IPA.ProgramLocation.QuadProgramLocation)
351          */
352         public Collection getNewInstanceTargets(QuadProgramLocation mc) {
353             // TODO
354             return null;
355         }
356         
357         public Collection/*<jq_Method>*/ getNewInstanceTargets(jq_Method n) {
358             if(PA.TRACE_REFLECTION) System.out.println("There are " + specs.size() + " specs to check against.");
359             for(Iterator iter = specs.iterator(); iter.hasNext();){
360                 NewInstanceTargets spec = (NewInstanceTargets) iter.next();
361                 if(PA.TRACE_REFLECTION) System.out.println("\tChecking against " + spec.getDeclaredIn());                
362                 
363                 if(spec.getDeclaredIn() == n){
364                     return spec.getTargets();
365                 }
366             }
367             if(PA.TRACE_REFLECTION){
368                 System.out.println("No information for method " + n);
369             }
370             return null;            
371         }
372     }  
373 }