Main Page   Packages   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members  

Decompiler.java

00001 package edu.ksu.cis.bandera.jjjc.decompiler;
00002 
00003 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
00004  * Bandera, a Java(TM) analysis and transformation toolkit           *
00005  * Copyright (C) 2000 Roby Joehanes (robbyjo@cis.ksu.edu)            *
00006  * All rights reserved.                                              *
00007  *                                                                   *
00008  * This work was done as a project in the SAnToS Laboratory,         *
00009  * Department of Computing and Information Sciences, Kansas State    *
00010  * University, USA (http://www.cis.ksu.edu/santos).                  *
00011  * It is understood that any modification not identified as such is  *
00012  * not covered by the preceding statement.                           *
00013  *                                                                   *
00014  * This work is free software; you can redistribute it and/or        *
00015  * modify it under the terms of the GNU Library General Public       *
00016  * License as published by the Free Software Foundation; either      *
00017  * version 2 of the License, or (at your option) any later version.  *
00018  *                                                                   *
00019  * This work is distributed in the hope that it will be useful,      *
00020  * but WITHOUT ANY WARRANTY; without even the implied warranty of    *
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU *
00022  * Library General Public License for more details.                  *
00023  *                                                                   *
00024  * You should have received a copy of the GNU Library General Public *
00025  * License along with this toolkit; if not, write to the             *
00026  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,      *
00027  * Boston, MA  02111-1307, USA.                                      *
00028  *                                                                   *
00029  * Java is a trademark of Sun Microsystems, Inc.                     *
00030  *                                                                   *
00031  * To submit a bug report, send a comment, or get the latest news on *
00032  * this project and other SAnToS projects, please visit the web-site *
00033  *                http://www.cis.ksu.edu/santos                      *
00034  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00035 import ca.mcgill.sable.soot.*;
00036 import ca.mcgill.sable.soot.jimple.*;
00037 import ca.mcgill.sable.util.*;
00038 
00039 import edu.ksu.cis.bandera.annotation.*;
00040 import edu.ksu.cis.bandera.abstraction.typeinference.*;
00041 import edu.ksu.cis.bandera.jjjc.*;
00042 import edu.ksu.cis.bandera.jjjc.node.*;
00043 
00044 import java.io.*;
00045 import java.util.*;
00046 /**
00047  * <P>Decompiler package is basically the main semi-decompiler class. All components of Bandera
00048  * works on Jimple-level, which format resembles the Java byte-code.
00049  * Sometimes, it is preferable to make the intermediate results, like after
00050  * slicing or abstraction available for user view. This is the need of Decompiler
00051  * package. Moreover, we definitely need the decompiler in order to use
00052  * JPF module checker since JPF input is in Java.
00053  *
00054  * <P>Why it is called a semi-decompiler? It is because of the usage of annotations
00055  * in decompiling the codes. The annotation created by the compiler, JJJC, is
00056  * used extensively to retrieve variable names and all sort of things so that
00057  * we don't need to deal directly with the Jimple, which makes things a lot
00058  * simpler.
00059  *
00060  * <P>Note that if you inspect this code, you'll see that this solution is somewhat
00061  * patchy. Yes, it is because other component may inline and do some optimizing
00062  * stuffs (and probably some other non-reversible actions) to the code. So, there is
00063  * no way that I can cover all cases (like jumping into an if block). This is it,
00064  * don't complain. If you don't like it, don't use it.
00065  *
00066  * <P>If you're interested on how the decompiler are used in Bandera, you should
00067  * only resort in Decompiler class, decompile method, in particular.
00068  * @author <a href="mailto:robbyjo@cis.ksu.edu">Roby Joehanes</a>
00069  * @version 0.4.21
00070 **/
00071 public class Decompiler {
00072     private Hashtable classes = null;
00073     static AnnotationManager annot = null;
00074     public static String fileSuffix = ".java";
00075     private String outputPath = ".";
00076     private String outFilename = "";
00077     private static Vector imports = new Vector();
00078     private DecompilerPrinter src = new DecompilerPrinter();
00079     private int lineCounter;
00080     private Hashtable lineToAnnotation = new Hashtable();
00081     static TypeTable typeTable = null;
00082 /**
00083  * You need to supply the hashtable of compiled classes, the annotation manager,
00084  * output path, and file suffix (usually <TT>".java"</TT>).<BR><BR>
00085  *
00086  * The usage is something like this:<BR>
00087  *    <TT>Slabs decompiler = new Slabs(classes, annotMgr, "/usr/pub/mydir", ".java");</TT><BR>
00088  * then:<BR>
00089  *    <TT>decompiler.decompile();</TT><BR>
00090  * You're done. Currently LineNumberToAnnotation is <b>not</b> implemented yet.
00091 **/
00092 public Decompiler(Hashtable cls, AnnotationManager am, String path, String suffix)
00093 {
00094     this(cls,am,path,suffix,null);
00095 }
00096 public Decompiler(Hashtable cls, AnnotationManager am, String path, String suffix, TypeTable tt)
00097 {
00098     classes = cls;
00099     annot = am;
00100     if (path != null)
00101     {
00102         outputPath = path;
00103         DecompilerPair.setOutputPath(path);
00104     }
00105     if (suffix != null) fileSuffix = suffix;
00106     typeTable = tt;
00107 }
00108 /**
00109  * This is meant to add some <TT>import</TT> statements
00110  * in order to resemble the original. This feature is
00111  * probably added whenever I have some time.
00112 **/
00113 public static void addImports(String cls)
00114 {
00115     if (!imports.contains(cls))
00116     {
00117         // Add the import statement
00118         imports.addElement(cls);
00119     }
00120 }
00121 /**
00122  * Decompiles all the classes. See constructor's notes
00123 */
00124 public void decompile()
00125 {
00126     System.out.println("Decompiling:");
00127     System.out.print("~~~~~~~~~~~~");
00128     for (Enumeration e = classes.elements(); e.hasMoreElements();)
00129     {
00130         SootClass sc = (SootClass) e.nextElement();
00131 
00132         dump(sc);
00133         // Reset the pretty printer
00134         src.reset();
00135 
00136         // Reset import statements
00137         imports = new Vector();
00138 
00139         // Determine the header of the Class
00140         DecompilerInfo.setClass(sc);
00141 
00142         String cn = DecompilerInfo.getClassName();
00143         String filePath = "";
00144 
00145         // Resolving package names
00146         if (cn.indexOf(".")!= -1)
00147         {
00148             int lastDot = cn.lastIndexOf(".");
00149             String className = cn.substring(lastDot+1);
00150             String pkgName = cn.substring(0,lastDot);
00151             StringTokenizer tok = new StringTokenizer(pkgName,".");
00152             StringBuffer buf = new StringBuffer();
00153             while (tok.hasMoreTokens())
00154             {
00155                 buf.append(tok.nextToken() + File.separator);
00156             }
00157             filePath = buf.toString();
00158             addImports("package "+pkgName);
00159             cn = className;
00160         }
00161 
00162         String curFileName = cn+"."+fileSuffix;
00163         System.out.println("\n"+curFileName);
00164         System.out.print("  *** ");
00165         curFileName = File.separator + curFileName;
00166 
00167         // Hack all methods, we still can't know the field since all
00168         // initializations are in <init> or <clinit> methods.
00169         Vector buf = new Vector();
00170         lineCounter = 1;
00171         Hashtable curTable = new Hashtable();
00172 
00173         for (ca.mcgill.sable.util.Iterator it = sc.getMethods().iterator(); it.hasNext();)
00174         {
00175             SootMethod sm = (SootMethod) it.next();
00176             Annotation ma = annot.getAnnotation(sc, sm);
00177 
00178             // Aware the parameter manager
00179             DecompilerInfo.setMethod(sm);
00180 
00181             // Reset the goto mapper
00182 //          DecompilerGotoMapper.reset();
00183 
00184             // Reset the temporary table
00185             DecompilerUtil.resetTempTable();
00186 
00187             // Decipher the annotations
00188             if (DecompilerInfo.isJimpleInitializer())
00189                 DecompilerUtil.setAllowField(true);
00190             else
00191                 DecompilerUtil.setAllowField(false);
00192 
00193             String curName = DecompilerInfo.getMethodName();
00194             System.out.print(curName+"; ");
00195             
00196             DecompilerSwitch.reset();
00197             if (!DecompilerInfo.isInterface() && !DecompilerInfo.isAbstract())
00198             {
00199                 Vector body = DecompilerSwitch.evaluate(ma);
00200                 String hdr = DecompilerInfo.getMethodDeclaration();
00201 
00202                 // Then print it!
00203                 if ((body.size()>=1) && (((String) body.get(body.size()-1)).equals("return;")))
00204                 {
00205                     body.removeElementAt(body.size()-1);
00206                 }
00207                 if (hdr.length()>0)
00208                 {
00209                     boolean isInit = DecompilerInfo.isInitializer();
00210                     if (((isInit) && ((body.size()>0) || (DecompilerInfo.getNoOfParam()>0))) || (!isInit))
00211                     {
00212                         Hashtable tbl = DecompilerSwitch.getLineToAnnotation();
00213                         if (body.size()>0)
00214                         {
00215                             String syn = (String) body.get(0);
00216                             if ((syn.startsWith("synchronized (this)")) || (syn.startsWith("synchronized (java.lang.Class.forName(")))
00217 //                          if (syn.startsWith("synchronized (java.lang.Class.forName("))
00218                             {
00219                                 DecompilerInfo.setSynchro(true);
00220                                 hdr = DecompilerInfo.getMethodDeclaration();
00221                                 body.removeElementAt(0);
00222                                 body.removeElementAt(0);
00223                                 body.removeElementAt(body.size()-1);
00224                                 renumber(tbl, -2);
00225                             }
00226                         }
00227                         // Add method annotation too
00228                         curTable.put(new DecompilerPair(lineCounter), ma);
00229                         lineCounter += 2;
00230                         buf.add("");
00231                         buf.add(hdr);
00232                         buf.add("{");
00233                         renumber(tbl,lineCounter);
00234                         curTable.putAll(tbl);
00235                         buf.addAll(body);
00236                         lineCounter += body.size()+2;
00237                         buf.add("}");
00238                     }
00239                 }
00240             } else
00241             {
00242                 // Abstract or Interface
00243                 curTable.put(new DecompilerPair(lineCounter), ma);
00244                 lineCounter++;
00245                 String hdr = DecompilerInfo.getMethodDeclaration();
00246                 buf.add(hdr+";");
00247             }
00248         }
00249 
00250         int importSize = imports.size();
00251         if (importSize > 0)
00252         {
00253             renumber(curTable, importSize+1);
00254             src.println(printImports());
00255         }
00256         src.println(DecompilerInfo.getClassDeclaration());
00257         src.println("{");
00258 
00259         // After all methods are hacked, the fields should be filled up with
00260         // default values. So, fields should be able to be printed now.
00261         Vector fieldDeclaration = DecompilerInfo.getFieldDeclaration();
00262         renumber(curTable, fieldDeclaration.size()+3);
00263         src.add(fieldDeclaration);
00264 
00265         // Then print all the methods we've processed so far.
00266         src.add(buf);
00267         src.println("}");
00268 
00269         // Add the class declaration annotation.
00270         Annotation ann = annot.getAnnotation(sc);
00271         if (importSize == 0)
00272             curTable.put(new DecompilerPair(1),ann);
00273         else
00274             curTable.put(new DecompilerPair(2+importSize),ann);
00275 
00276         // Merge the table
00277         lineToAnnotation.putAll(curTable);
00278 
00279         // Prepare the file to dump the output
00280         String completeName = outputPath + filePath + curFileName;
00281         try
00282         {
00283             PrintStream javaPrint = new PrintStream(new FileOutputStream(new File(completeName)));
00284             javaPrint.print(src.toString());
00285             javaPrint.close();
00286         } catch (IOException ex)
00287         {
00288             throw new RuntimeException("Cannot create file '" + completeName + "'\n");
00289         }
00290     }
00291     System.out.println("\nDecompiler Done");
00292     System.out.println("~~~~~~~~~~~~~~~");
00293 
00294     // Dump Annotation Table...
00295 /*  Hashtable tbl = getLineToAnnotationTable();
00296 
00297     for (Enumeration e = tbl.keys(); e.hasMoreElements();)
00298     {
00299         DecompilerPair p = (DecompilerPair) e.nextElement();
00300         Annotation an = (Annotation) tbl.get(p);
00301         if (an != null)
00302         System.out.println(p.toString()+" = "+an.toString());
00303         else
00304         System.out.println(p.toString()+" = null");
00305     }*/
00306 }
00307 /**
00308    *
00309    */
00310 private void dump(SootClass sc) {
00311     StoredBody bd = new StoredBody(ca.mcgill.sable.soot.jimple.Jimple.v());
00312     String className = sc.getName();
00313     try {
00314         java.io.File jimpFile = new java.io.File(outputPath + File.separator + className + ".decompiled.jimple");
00315         java.io.FileOutputStream jimpOut = new java.io.FileOutputStream(jimpFile);
00316         sc.printTo(bd, new java.io.PrintWriter(jimpOut, true));
00317         jimpOut.close();
00318     } catch (java.io.IOException ex) {
00319         System.out.println("***Can't dump! "+ex.getMessage()+"\n");
00320     }
00321 }
00322 public Hashtable getLineToAnnotationTable()
00323 {
00324     Hashtable tbl = new Hashtable();
00325     tbl.putAll(lineToAnnotation);
00326     return tbl;
00327 }
00328 private String printImports()
00329 {
00330     StringBuffer s = new StringBuffer();
00331     
00332     for (Enumeration e = imports.elements(); e.hasMoreElements(); )
00333     {
00334         String str = (String) e.nextElement();
00335         if (str.startsWith("package ")) s.append(str + ";\n");
00336             else s.append("import "+ str +";\n");
00337     }
00338     return s.toString();
00339 }
00340 private void renumber(Hashtable tbl, int offset)
00341 {
00342     Hashtable newTbl = new Hashtable();
00343     
00344     for (java.util.Iterator i = tbl.entrySet().iterator(); i.hasNext();) {
00345         java.util.Map.Entry entry = (java.util.Map.Entry) i.next();
00346         DecompilerPair p = (DecompilerPair) entry.getKey();
00347         Annotation an = (Annotation) entry.getValue();
00348         p.addBy(offset);
00349         newTbl.put(p, an);
00350     }
00351     tbl.clear();
00352     tbl.putAll(newTbl);
00353 }
00354 }

Generated at Thu Feb 7 06:43:24 2002 for Bandera by doxygen1.2.10 written by Dimitri van Heesch, © 1997-2001