PageRenderTime 54ms CodeModel.GetById 2ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 0ms

/src/kilim/tools/Weaver.java

http://github.com/kilim/kilim
Java | 325 lines | 224 code | 33 blank | 68 comment | 49 complexity | 90550abedb33b4a54cdc3753e8e5e2c4 MD5 | raw file
  1/* Copyright (c) 2006, Sriram Srinivasan, 2016 nqzero
  2 *
  3 * You may distribute this software under the terms of the license 
  4 * specified in the file "License"
  5 */
  6
  7package kilim.tools;
  8
  9import kilim.analysis.KilimContext;
 10import java.io.BufferedInputStream;
 11import java.io.ByteArrayInputStream;
 12import java.io.File;
 13import java.io.FileInputStream;
 14import java.io.FileOutputStream;
 15import java.io.IOException;
 16import java.io.InputStream;
 17import java.net.URL;
 18import java.net.URLClassLoader;
 19import java.util.ArrayList;
 20import java.util.List;
 21import java.util.regex.Pattern;
 22
 23import kilim.KilimException;
 24import kilim.WeavingClassLoader;
 25import kilim.analysis.ClassInfo;
 26import kilim.analysis.ClassWeaver;
 27import kilim.analysis.FileLister;
 28import kilim.mirrors.CachedClassMirrors;
 29
 30/**
 31 * This class supports both command-line and run time weaving of Kilim bytecode. 
 32 */
 33
 34public class Weaver {
 35    public static String outputDir = null;
 36    public static boolean verbose = true;
 37    public static boolean force = false;
 38    public static boolean proxy = true;
 39    public static Pattern excludePattern = null;
 40    static int err = 0;
 41
 42    public KilimContext context;
 43    public Weaver(KilimContext $context) {
 44        context = $context==null ? new KilimContext() : $context;
 45    }
 46    
 47    
 48    
 49    
 50    /**
 51     * <pre>
 52     * Usage: java kilim.tools.Weaver -d &lt;output directory&gt; {source classe, jar, directory ...}
 53     * </pre>
 54     * 
 55     * If directory names or jar files are given, all classes in that container are processed. It is
 56     * perfectly fine to specify the same directory for source and output like this: 
 57     * <pre>
 58     *    java kilim.tools.Weaver -d ./classes ./classes
 59     * </pre>
 60     * 
 61     * by default, each element is added to the classpath (use -c to suppress classpath augmentation)
 62     * 
 63     * arguments:
 64     * <ul>
 65     * <li>-d directory: write output to directory (required)</li>
 66     * <li>-f: force, write output even if output file is newer than source</li>
 67     * <li>-c: don't add source class list to the classpath</li>
 68     * <li>-h: print help info</li>
 69     * <li>-q: quiet</li>
 70     * <li>-x regex: exclude, skip classes matching regex</li>
 71     * </ul>
 72     * 
 73     * Ensure that all classes to be woven are in the classpath. The output directory does not have to be 
 74     * in the classpath during weaving.
 75     *   
 76     * @see #weave(List) for run-time weaving.
 77     */
 78    public static void main(String[] args) throws IOException {
 79        ArrayList<String> names = parseArgs(args);
 80        doMain(names.toArray(new String [] {}),null);
 81        if (err > 0) System.exit(err);
 82    }
 83    private static String [] concat(String [] a,String [] b) {
 84        String [] c = new String[a.length + b.length];
 85        System.arraycopy(a,0,c,0,a.length);
 86        System.arraycopy(b,0,c,a.length,b.length);
 87        return c;
 88    }
 89    
 90    public static int doMain(String [] names,String [] classpath) throws IOException {
 91        mkdir(outputDir);
 92        
 93        Weaver weaver;
 94        if (proxy) {
 95            ClassLoader current = Weaver.class.getClassLoader();
 96            String [] composite = classpath==null ? names : concat(names,classpath);
 97            URL [] paths = WeavingClassLoader.getURLs(composite);
 98            CachedClassMirrors ccm = new CachedClassMirrors(new URLClassLoader(paths,current));
 99            weaver = new Weaver(new KilimContext(ccm));
100        }
101        else
102            weaver = new Weaver(null);
103
104        String currentName = null;
105        for (String name : names) {
106            try {
107                if (name.endsWith(".class")) {
108                    if (exclude(name))
109                        continue;
110                    currentName = name;
111                    weaver.weaveFile(name, new BufferedInputStream(new FileInputStream(name)));
112                } else if (name.endsWith(".jar")) {
113                    for (FileLister.Entry fe : new FileLister(name)) {
114                        currentName = fe.getFileName();
115                        if (currentName.endsWith(".class")) {
116                            currentName = currentName.substring(0, currentName.length() - 6)
117                                    .replace('/', '.');
118                            if (exclude(currentName))
119                                continue;
120                            weaver.weaveFile(currentName, fe.getInputStream());
121                        }
122                    }
123                } else if (new File(name).isDirectory()) {
124                    for (FileLister.Entry fe : new FileLister(name)) {
125                        currentName = fe.getFileName();
126                        if (currentName.endsWith(".class")) {
127                            if (exclude(currentName))
128                                continue;
129                            if (!force && fe.check(outputDir))
130                                continue;
131                            weaver.weaveFile(currentName, fe.getInputStream());
132                        }
133                    }
134                } else {
135                    System.out.println("skipping class (support removed): " + name);
136                }
137            } catch (KilimException ke) {
138                System.err.println("Error weaving " + currentName + ". " + ke.getMessage());
139                // ke.printStackTrace();
140                System.exit(1);
141            } catch (IOException ioe) {
142                System.err.println("Unable to find/process '" + currentName + "'");
143                System.exit(1);
144            } catch (Throwable t) {
145                System.err.println("Error weaving " + currentName);
146                t.printStackTrace();
147                System.exit(1);
148            }
149        }
150        return err;
151    }
152
153    static boolean exclude(String name) {
154        return excludePattern == null ? false : excludePattern.matcher(name).find();
155    }
156
157    // non-static to allow easy usage from alternative classloaders
158    public ClassWeaver weave(InputStream is) {
159        ClassWeaver cw = null;
160        if (is==null) return null;
161        try {
162            cw = new ClassWeaver(context,is);
163            cw.weave();
164            if (outputDir != null)
165                writeClasses(cw);
166        }
167        catch (IOException ex) {}
168        return cw;
169    }
170
171    public void weaveFile(String name, InputStream is) throws IOException {
172        try {
173            ClassWeaver cw = new ClassWeaver(context,is);
174            cw.weave();
175            writeClasses(cw);
176        } catch (KilimException ke) {
177            System.err.println("***** Error weaving " + name + ". " + ke.getMessage());
178            // ke.printStackTrace();
179            err = 1;
180        } catch (RuntimeException re) {
181            System.err.println("***** Error weaving " + name + ". " + re.getMessage());
182            re.printStackTrace();
183            err = 1;
184        } catch (IOException ioe) {
185            err = 1;
186            System.err.println("***** Unable to find/process '" + name + "'\n" + ioe.getMessage());
187        }
188    }
189
190
191
192    static void writeClasses(ClassWeaver cw) throws IOException {
193        List<ClassInfo> cis = cw.getClassInfos();
194        if (cis.size() > 0) {
195            for (ClassInfo ci : cis) {
196                writeClass(ci);
197            }
198        }
199    }
200
201    public static void writeClass(ClassInfo ci) throws IOException {
202        String className = ci.className.replace('.', File.separatorChar);
203        String dir = outputDir + File.separatorChar + getDirName(className);
204        mkdir(dir);
205        // Convert name to fully qualified file name
206        className = outputDir + File.separatorChar + className + ".class";
207        if (ci.className.startsWith("kilim.S_")) {
208            // Check if we already have that file
209            if (new File(className).exists())
210                return;
211        }
212        FileOutputStream fos = new FileOutputStream(className);
213        fos.write(ci.bytes);
214        fos.close();
215        if (verbose) {
216            System.out.println("Wrote: " + className);
217        }
218    }
219
220    static void mkdir(String dir) throws IOException {
221        File f = new File(dir);
222        if (!f.exists()) {
223            if (!f.mkdirs()) {
224                throw new IOException("Unable to create directory: " + dir);
225            }
226        }
227    }
228
229    static String getDirName(String className) {
230        int end = className.lastIndexOf(File.separatorChar);
231        return (end == -1) ? "" : className.substring(0, end);
232    }
233
234    static void help() {
235        System.err.println("java kilim.tools.Weaver opts -d <outputDir> (class/directory/jar)+");
236        System.err.println("   where opts are   -q : quiet");
237        System.err.println("                    -x <regex> : exclude all classes matching regex");
238        System.err.println("                    -f         : weave even if up to date");
239        System.err.println("                    -c         : don't add targets to classpath");
240        System.exit(1);
241    }
242
243    public static ArrayList<String> parseArgs(String[] args) throws IOException {
244        if (args.length == 0)
245            help();
246
247        ArrayList<String> ret = new ArrayList<String>(args.length);
248        String regex = null;
249        for (int i = 0; i < args.length; i++) {
250            String arg = args[i];
251            if (arg.equals("-d")) {
252                outputDir = args[++i];
253            } else if (arg.equals("-q")) {
254                verbose = false;
255            } else if (arg.equals("-f")) {
256                force = true;
257            } else if (arg.equals("-c")) {
258                proxy = false;
259            } else if (arg.equals("-h")) {
260                help();
261            } else if (arg.equals("-x")) {
262                regex = args[++i];
263                excludePattern = Pattern.compile(regex);
264            } else {
265                ret.add(arg);
266            }
267        }
268        if (outputDir == null) {
269            System.err.println("Specify output directory with -d option");
270            System.exit(1);
271        }
272        return ret;
273    }
274
275
276
277    /**
278     * Analyzes the list of supplied classes and inserts Kilim-related bytecode if necessary. If a
279     * supplied class is dependent upon another class X, it is the caller's responsibility to ensure
280     * that X is either in the classpath, or loaded by the context classloader, or has been seen in
281     * an earlier invocation of weave().  
282     * 
283     * Since weave() remembers method signatures from earlier invocations, the woven classes do not
284     * have to be classloaded to help future invocations of weave. 
285     * 
286     * If two classes A and B are not in the classpath, and are mutually recursive, they can be woven
287     * only if supplied in the same input list.
288     *  
289     * This method is thread safe.
290     * 
291     * @param classes A list of (className, byte[]) pairs. The first part is a fully qualified class
292     *            name, and the second part is the bytecode for the class.
293     * 
294     * @return A list of (className, byte[]) pairs. Some of the classes may or may not have been
295     *         modified, and new ones may be added.
296     * 
297     * @throws KilimException
298     */
299    public List<ClassInfo> weave(List<ClassInfo> classes) throws KilimException, IOException {
300        // save the detector attached to this thread, if any. It will be restored
301        // later.
302        ArrayList<ClassInfo> ret = new ArrayList<ClassInfo>(classes.size());
303        try {
304            // First cache all the method signatures from the supplied classes to allow
305            // the weaver to lookup method signatures from mutually recursive classes.
306            for (ClassInfo cl : classes) {
307                context.detector.mirrors.mirror(cl.bytes);
308            }
309
310            // Now weave them individually
311            for (ClassInfo cl : classes) {
312                InputStream is = new ByteArrayInputStream(cl.bytes);
313                ClassWeaver cw = new ClassWeaver(context,is);
314                cw.weave();
315                ret.addAll(cw.getClassInfos()); // one class file can result in multiple classes
316            }
317            return ret;
318        } finally {
319        }
320    }
321
322    
323    
324
325}