1 cananian 1.1.2.1 // Lint.java, created Sun Sep 12 09:49:13 1999 by cananian
  2 cananian 1.1.2.1 // Copyright (C) 1999 C. Scott Ananian <cananian@alumni.princeton.edu>
  3 cananian 1.1.2.1 // Licensed under the terms of the GNU GPL; see COPYING for details.
  4 cananian 1.1.2.1 package harpoon.Main;
  5 cananian 1.1.2.1 
  6 cananian 1.1.2.1 import harpoon.ClassFile.HClass;
  7 cananian 1.1.2.1 import harpoon.ClassFile.HCode;
  8 cananian 1.1.2.3 import harpoon.ClassFile.HCodeElement;
  9 cananian 1.1.2.1 import harpoon.ClassFile.HCodeFactory;
 10 cananian 1.1.2.1 import harpoon.ClassFile.HMethod;
 11 cananian 1.1.2.5 import harpoon.ClassFile.Linker;
 12 cananian 1.1.2.1 import harpoon.ClassFile.Loader;
 13 cananian 1.1.2.1 import harpoon.Temp.Temp;
 14 cananian 1.1.2.1 
 15 cananian 1.1.2.3 import java.io.BufferedReader;
 16 cananian 1.1.2.3 import java.io.File;
 17 cananian 1.1.2.3 import java.io.FileReader;
 18 cananian 1.1.2.3 import java.util.ArrayList;
 19 cananian 1.1.2.1 import java.util.Iterator;
 20 cananian 1.1.2.3 import java.util.List;
 21 cananian 1.1.2.1 /**
 22 cananian 1.1.2.1  * <code>Lint</code> is a quick Java code-checker.<p>
 23 cananian 1.1.2.1  * Currently it checks only two things:<ul>
 24 cananian 1.1.2.1  * <li>Uses of <code>Label</code><code>.toString()</code> (which should
 25 cananian 1.1.2.1  *     almost always be <code>Label.name</code> instead), and
 26 cananian 1.1.2.1  * <li>Testing objects for equality using <code>==</code> (instead of
 27 cananian 1.1.2.1  *     <code>equals()</code>).
 28 cananian 1.1.2.1  * </ul><p>
 29 cananian 1.1.2.1  * Invoke with:<pre>
 30 cananian 1.1.2.1  * java harpoon.Main.Lint [package1] [package2] ...
 31 cananian 1.1.2.1  * </pre><p>
 32 cananian 1.1.2.1  * Output is a list of source files and line numbers which are being
 33 cananian 1.1.2.1  * flagged as possibly incorrect.
 34 cananian 1.1.2.1  * 
 35 cananian 1.1.2.1  * @author  C. Scott Ananian <cananian@alumni.princeton.edu>
 36 cananian 1.3      * @version $Id: Lint.java,v 1.3 2004/02/08 01:58:13 cananian Exp $
 37 cananian 1.1.2.1  */
 38 cananian 1.1.2.1 public abstract class Lint extends harpoon.IR.Registration {
 39 cananian 1.1.2.5     public final static Linker linker = Loader.systemLinker;
 40 cananian 1.1.2.5     
 41 cananian 1.1.2.4     public static void usage(String errmsg) {
 42 cananian 1.1.2.4         System.err.println(errmsg);
 43 cananian 1.1.2.4         System.err.println("Usage: java "+Lint.class.getName()+" "+
 44 cananian 1.1.2.4                            "{-use sourcepath} {-e} {-v} {-l} "+
 45 cananian 1.1.2.4                            "[packages] ...");
 46 cananian 1.1.2.4         System.err.println("     -e\tShow possibly-incorrect object comparisons using ==");
 47 cananian 1.1.2.4         System.err.println("     -v\tShow possibly-incorrect QuadVisitor.visit(CALL) methods");
 48 cananian 1.1.2.4         System.err.println("     -l\tShow uses of (deprecated) Label.toString()");
 49 cananian 1.1.2.4         System.exit(1);
 50 cananian 1.1.2.4     }
 51 cananian 1.1.2.1     public static void main(String[] args) {
 52 cananian 1.1.2.4         boolean check_visit=false,check_labels=false,check_equals=false;
 53 cananian 1.1.2.3         // Command-line should have list of packages, & opt. sourcepath
 54 cananian 1.1.2.4         int s;
 55 cananian 1.1.2.4         for (s=0; args.length>s && args[s].charAt(0)=='-'; s++) {
 56 cananian 1.1.2.4             if (args[s].startsWith("-use") && args.length>s+1)
 57 cananian 1.1.2.4                 SourceLineReader.sourcepath=args[++s];
 58 cananian 1.1.2.4             else if (args[s].startsWith("-v"))
 59 cananian 1.1.2.4                 check_visit=true;
 60 cananian 1.1.2.4             else if (args[s].startsWith("-l"))
 61 cananian 1.1.2.4                 check_labels=true;
 62 cananian 1.1.2.4             else if (args[s].startsWith("-e"))
 63 cananian 1.1.2.4                 check_equals=true;
 64 cananian 1.1.2.4             else
 65 cananian 1.1.2.4                 usage("Unrecognized option: "+args[s]);
 66 cananian 1.1.2.4         }
 67 cananian 1.1.2.4         if (!(args.length>s))
 68 cananian 1.1.2.4             usage("No packages specified.");
 69 cananian 1.1.2.4 
 70 cananian 1.1.2.4         HCodeFactory hcf = harpoon.IR.Bytecode.Code.codeFactory();
 71 cananian 1.1.2.4         // rule-checkers using any IR at all.
 72 cananian 1.1.2.4         if (check_visit) // check implementations of visit(CALL)
 73 cananian 1.1.2.4             hcf = new CheckVisitCALL(hcf);
 74 cananian 1.1.2.4         // rule-checkers using quad-no-ssa
 75 cananian 1.1.2.4         if (check_labels) // check uses of Label.toString()
 76 cananian 1.1.2.4             hcf = new CheckLabels(hcf);
 77 cananian 1.1.2.4         // rule-checkers using quad-ssi
 78 cananian 1.1.2.4         if (check_equals) // check usage of == for objects.
 79 cananian 1.1.2.4             hcf = new CheckEquals(hcf);
 80 cananian 1.1.2.4 
 81 cananian 1.1.2.3         for (int i=s; i<args.length; i++) {
 82 cananian 1.1.2.1             System.err.println("CHECKING PACKAGE "+args[i]);
 83 cananian 1.1.2.1             for (Iterator it=Loader.listClasses(args[i]); it.hasNext(); ) {
 84 cananian 1.1.2.5                 HClass hc = linker.forName((String)it.next());
 85 cananian 1.1.2.1                 System.err.println(" - " + hc);
 86 cananian 1.1.2.1                 HMethod[] hms = hc.getDeclaredMethods();
 87 cananian 1.1.2.2                 for (int j=0; j<hms.length; j++) {
 88 cananian 1.1.2.1                     hcf.convert(hms[j]);
 89 cananian 1.1.2.2                     hcf.clear(hms[j]); // free memory.
 90 cananian 1.1.2.2                 }
 91 cananian 1.1.2.1             }
 92 cananian 1.1.2.1         }
 93 cananian 1.1.2.1     }
 94 cananian 1.1.2.3     /** Allow reference to particular lines of a class file */
 95 cananian 1.1.2.3     static class SourceLineReader {
 96 cananian 1.1.2.3         public static String sourcepath=".";
 97 cananian 1.1.2.3         private List linelist = new ArrayList();
 98 cananian 1.1.2.3         SourceLineReader(HClass cls, String sourcefile) {
 99 cananian 1.1.2.3             String filesep = System.getProperty("file.separator");
100 cananian 1.1.2.3             String packagepath =
101 cananian 1.1.2.3                 cls.getPackage().replace('.',filesep.charAt(0));
102 cananian 1.1.2.3             File f = new File(new File(sourcepath,packagepath),sourcefile);
103 cananian 1.1.2.3             try {
104 cananian 1.1.2.3             BufferedReader br = new BufferedReader(new FileReader(f));
105 cananian 1.1.2.3             for (String line = br.readLine(); line!=null; line=br.readLine())
106 cananian 1.1.2.3                 linelist.add(line);
107 cananian 1.1.2.3             br.close();
108 cananian 1.1.2.3             } catch (java.io.IOException e) { linelist=null; }
109 cananian 1.1.2.3         }
110 cananian 1.1.2.3         public String getLine(int lineno) {
111 cananian 1.1.2.3             if (linelist==null || linelist.size()<lineno)
112 cananian 1.1.2.3                 return "[--- unable to read file ---]";
113 cananian 1.1.2.3             return (String) linelist.get(lineno-1);
114 cananian 1.1.2.3         }
115 cananian 1.1.2.3     }
116 cananian 1.1.2.3     /** method for caching sourcelinereaders */
117 cananian 1.1.2.3     public static SourceLineReader getSLR(HClass cls, String sourcefile) {
118 cananian 1.1.2.3         if (last_slr==null || last_cls!=cls || last_sourcefile!=sourcefile) {
119 cananian 1.1.2.3             last_slr = new SourceLineReader(cls, sourcefile);
120 cananian 1.1.2.3             last_cls = cls; last_sourcefile=sourcefile;
121 cananian 1.1.2.3         }
122 cananian 1.1.2.3         return last_slr;
123 cananian 1.1.2.3     }
124 cananian 1.1.2.3     static SourceLineReader last_slr = null;
125 cananian 1.1.2.3     static HClass last_cls = null;
126 cananian 1.1.2.3     static String last_sourcefile = null;
127 cananian 1.1.2.3     /** print out a standardized error message */
128 cananian 1.1.2.3     public static void printError(String msg, HMethod hm, HCodeElement hce) {
129 cananian 1.1.2.3         System.out.println("WARNING: "+msg+" in " +
130 cananian 1.1.2.3                            hm.getDeclaringClass().getName()+"."+hm.getName() +
131 cananian 1.1.2.3                            ":");
132 cananian 1.1.2.3         System.out.println(hce.getSourceFile()+"("+hce.getLineNumber()+"): " +
133 cananian 1.1.2.3                            getSLR(hm.getDeclaringClass(), hce.getSourceFile())
134 cananian 1.1.2.3                            .getLine(hce.getLineNumber()).trim());
135 cananian 1.1.2.3         System.out.println();
136 cananian 1.1.2.3     }
137 cananian 1.1.2.3 
138 cananian 1.1.2.3     /////////// RULE-CHECKING CODE FACTORIES:
139 cananian 1.1.2.3 
140 cananian 1.1.2.4     /** Find all methods overriding QuadVisitor.visit(CALL q). */
141 cananian 1.1.2.4     static class CheckVisitCALL implements HCodeFactory {
142 cananian 1.1.2.4         final HCodeFactory hcf;
143 cananian 1.1.2.4         CheckVisitCALL(HCodeFactory hcf) { this.hcf = hcf; }
144 cananian 1.1.2.4         public void clear(HMethod hm) { hcf.clear(hm); }
145 cananian 1.1.2.4         public String getCodeName() { return hcf.getCodeName(); }
146 cananian 1.1.2.4         public HCode convert(HMethod hm) {
147 cananian 1.1.2.4             HCode c = hcf.convert(hm);
148 cananian 1.1.2.4             if (c==null) return c;
149 cananian 1.1.2.4             if (hm.getDeclaringClass().isInstanceOf(HCqv) &&
150 cananian 1.1.2.4                 hm.getName().equals(HMvC.getName()) &&
151 cananian 1.1.2.4                 hm.getDescriptor().equals(HMvC.getDescriptor()))
152 cananian 1.1.2.4                 Lint.printError("Possibly incorrect implementation of "+
153 cananian 1.1.2.4                                 "QuadVisitor.visit(CALL q)",
154 cananian 1.1.2.4                                 hm, c.getRootElement());
155 cananian 1.1.2.4             return c;
156 cananian 1.1.2.4         }
157 cananian 1.1.2.4         private static final HClass HCqv =
158 cananian 1.1.2.5             linker.forName("harpoon.IR.Quads.QuadVisitor");
159 cananian 1.1.2.4         private static final HMethod HMvC =
160 cananian 1.1.2.4             HCqv.getMethod("visit", new HClass[]
161 cananian 1.1.2.5                            { linker.forName("harpoon.IR.Quads.CALL") } );
162 cananian 1.1.2.4     }
163 cananian 1.1.2.4 
164 cananian 1.1.2.1     /** Find all places where Label.toString() is called. */
165 cananian 1.1.2.1     static class CheckLabels implements HCodeFactory {
166 cananian 1.1.2.1         final HCodeFactory hcf;
167 cananian 1.1.2.4         CheckLabels(HCodeFactory hcf) {
168 cananian 1.1.2.4             if (!hcf.getCodeName().equals(harpoon.IR.Quads.QuadNoSSA.codename))
169 cananian 1.1.2.4                 hcf = harpoon.IR.Quads.QuadNoSSA.codeFactory(hcf);
170 cananian 1.1.2.4             this.hcf = hcf;
171 cananian 1.1.2.4         }
172 cananian 1.1.2.1         public void clear(HMethod hm) { hcf.clear(hm); }
173 cananian 1.1.2.1         public String getCodeName() { return hcf.getCodeName(); }
174 cananian 1.1.2.1         public HCode convert(HMethod hm) {
175 cananian 1.1.2.1             HCode c = hcf.convert(hm);
176 cananian 1.1.2.1             if (c==null) return c;
177 cananian 1.1.2.1             for (Iterator it=c.getElementsI(); it.hasNext(); ) {
178 cananian 1.1.2.1                 harpoon.IR.Quads.Quad q = (harpoon.IR.Quads.Quad) it.next();
179 cananian 1.1.2.1                 if (q instanceof harpoon.IR.Quads.CALL &&
180 cananian 1.1.2.1                     ((harpoon.IR.Quads.CALL)q).method().equals(label_toString))
181 cananian 1.1.2.3                     Lint.printError("Use of deprecated Label.toString() method",
182 cananian 1.1.2.3                                hm, q);
183 cananian 1.1.2.1             }
184 cananian 1.1.2.1             return c;
185 cananian 1.1.2.1         }
186 cananian 1.1.2.1         private static final HMethod label_toString =
187 cananian 1.1.2.5             linker.forName("harpoon.Temp.Label")
188 cananian 1.1.2.1                   .getMethod("toString",new HClass[0]);
189 cananian 1.1.2.1     }
190 cananian 1.1.2.1     /** Find all places where object equality is checked with == and
191 cananian 1.1.2.1      *  where we're not comparing something against null. */
192 cananian 1.1.2.1     static class CheckEquals implements HCodeFactory {
193 cananian 1.1.2.1         final HCodeFactory hcf;
194 cananian 1.1.2.4         CheckEquals(HCodeFactory hcf) {
195 cananian 1.1.2.4             if (!hcf.getCodeName().equals(harpoon.IR.Quads.QuadSSI.codename))
196 cananian 1.1.2.4                 hcf = harpoon.IR.Quads.QuadSSI.codeFactory(hcf);
197 cananian 1.1.2.4             this.hcf = hcf;
198 cananian 1.1.2.4         }
199 cananian 1.1.2.1         public void clear(HMethod hm) { hcf.clear(hm); }
200 cananian 1.1.2.1         public String getCodeName() { return hcf.getCodeName(); }
201 cananian 1.1.2.1         public HCode convert(HMethod hm) {
202 cananian 1.1.2.1             HCode c = hcf.convert(hm);
203 cananian 1.1.2.1             if (c==null) return c;
204 cananian 1.1.2.2             harpoon.Analysis.Quads.SCC.SCCAnalysis tm =
205 cananian 1.1.2.1                 new harpoon.Analysis.Quads.SCC.SCCAnalysis(c);
206 cananian 1.1.2.2             new harpoon.Analysis.Quads.SCC.SCCOptimize(tm).optimize(c);
207 cananian 1.1.2.1             for (Iterator it=c.getElementsI(); it.hasNext(); ) {
208 cananian 1.1.2.1                 harpoon.IR.Quads.Quad q = (harpoon.IR.Quads.Quad) it.next();
209 cananian 1.1.2.1                 if (!(q instanceof harpoon.IR.Quads.OPER)) continue;
210 cananian 1.1.2.1                 harpoon.IR.Quads.OPER oper = (harpoon.IR.Quads.OPER)q;
211 cananian 1.1.2.1                 if (oper.opcode()!=harpoon.IR.Quads.Qop.ACMPEQ) continue;
212 cananian 1.1.2.1                 Temp l=oper.operands(0), r=oper.operands(1);
213 cananian 1.1.2.1                 if (tm.typeMap(q,l)==HClass.Void ||
214 cananian 1.1.2.1                     tm.typeMap(q,r)==HClass.Void) continue;
215 cananian 1.1.2.3                 if (refunique.isSuperinterfaceOf(tm.typeMap(q,l)) ||
216 cananian 1.1.2.3                     refunique.isSuperinterfaceOf(tm.typeMap(q,r))) continue;
217 cananian 1.1.2.6                 Lint.printError("Possibly incorrect use of == ("+tm.typeMap(q,l)+" / "+tm.typeMap(q,r)+")", hm, q);
218 cananian 1.1.2.1             }
219 cananian 1.1.2.1             return c;
220 cananian 1.1.2.1         }
221 cananian 1.1.2.3         private static final HClass refunique =
222 cananian 1.3                 linker.forName("net.cscott.jutil.ReferenceUnique");
223 cananian 1.1.2.1     }
224 cananian 1.2     }