make/tools/GenStubs/GenStubs.java

changeset 441
4325b440eb3e
child 468
51011e02c02f
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/make/tools/GenStubs/GenStubs.java	Fri Nov 20 11:18:43 2009 -0800
     1.3 @@ -0,0 +1,392 @@
     1.4 +/*
     1.5 + * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
     1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.7 + *
     1.8 + * This code is free software; you can redistribute it and/or modify it
     1.9 + * under the terms of the GNU General Public License version 2 only, as
    1.10 + * published by the Free Software Foundation.  Sun designates this
    1.11 + * particular file as subject to the "Classpath" exception as provided
    1.12 + * by Sun in the LICENSE file that accompanied this code.
    1.13 + *
    1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    1.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    1.17 + * version 2 for more details (a copy is included in the LICENSE file that
    1.18 + * accompanied this code).
    1.19 + *
    1.20 + * You should have received a copy of the GNU General Public License version
    1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    1.23 + *
    1.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    1.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
    1.26 + * have any questions.
    1.27 + */
    1.28 +import java.io.*;
    1.29 +import java.util.*;
    1.30 +import javax.tools.JavaFileObject;
    1.31 +import javax.tools.StandardJavaFileManager;
    1.32 +import javax.tools.StandardLocation;
    1.33 +
    1.34 +import org.apache.tools.ant.BuildException;
    1.35 +import org.apache.tools.ant.DirectoryScanner;
    1.36 +import org.apache.tools.ant.taskdefs.MatchingTask;
    1.37 +import org.apache.tools.ant.types.Path;
    1.38 +import org.apache.tools.ant.types.Reference;
    1.39 +
    1.40 +
    1.41 +import com.sun.source.tree.CompilationUnitTree;
    1.42 +import com.sun.source.util.JavacTask;
    1.43 +import com.sun.tools.javac.api.JavacTool;
    1.44 +import com.sun.tools.javac.code.Flags;
    1.45 +import com.sun.tools.javac.code.TypeTags;
    1.46 +import com.sun.tools.javac.tree.JCTree;
    1.47 +import com.sun.tools.javac.tree.JCTree.JCBlock;
    1.48 +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
    1.49 +import com.sun.tools.javac.tree.JCTree.JCLiteral;
    1.50 +import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
    1.51 +import com.sun.tools.javac.tree.JCTree.JCModifiers;
    1.52 +import com.sun.tools.javac.tree.JCTree.JCStatement;
    1.53 +import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
    1.54 +import com.sun.tools.javac.tree.Pretty;
    1.55 +import com.sun.tools.javac.tree.TreeTranslator;
    1.56 +
    1.57 +/**
    1.58 + * Generate stub source files by removing implementation details from input files.
    1.59 + *
    1.60 + * This is a special purpose stub generator, specific to the needs of generating
    1.61 + * stub files for JDK 7 API that are needed to compile langtools files that depend
    1.62 + * on that API. The stub generator works by removing as much of the API source code
    1.63 + * as possible without affecting the public signature, in order to reduce the
    1.64 + * transitive closure of the API being referenced. The resulting stubs can be
    1.65 + * put on the langtools sourcepath with -implicit:none to compile the langtools
    1.66 + * files that depend on the JDK 7 API.
    1.67 + *
    1.68 + * Usage:
    1.69 + *  genstubs -s <outdir> -sourcepath <path> <classnames>
    1.70 + *
    1.71 + * The specified class names are looked up on the sourcepath, and corresponding
    1.72 + * stubs are written to the source output directory.
    1.73 + *
    1.74 + * Classes are parsed into javac ASTs, then processed with a javac TreeTranslator
    1.75 + * to remove implementation details, and written out in the source output directory.
    1.76 + * Documentation comments and annotations are removed. Method bodies are removed
    1.77 + * and methods are marked native. Private and package-private field definitions
    1.78 + * have their initializers replace with 0, 0.0, false, null as appropriate.
    1.79 + *
    1.80 + * An Ant task, Main$Ant is also provided. Files are specified with an implicit
    1.81 + * fileset, using srcdir as a base directory. The set of files to be included
    1.82 + * is specified with an includes attribute or nested <includes> set. However,
    1.83 + * unlike a normal fileset, an empty includes attribute means "no files" instead
    1.84 + * of "all files".  The Ant task also accepts "fork=true" and classpath attribute
    1.85 + * or nested <classpath> element to run GenStubs in a separate VM with the specified
    1.86 + * path. This is likely necessary if a JDK 7 parser is required to read the
    1.87 + * JDK 7 input files.
    1.88 + */
    1.89 +
    1.90 +public class GenStubs {
    1.91 +    static class Fault extends Exception {
    1.92 +        private static final long serialVersionUID = 0;
    1.93 +        Fault(String message) {
    1.94 +            super(message);
    1.95 +        }
    1.96 +        Fault(String message, Throwable cause) {
    1.97 +            super(message);
    1.98 +            initCause(cause);
    1.99 +        }
   1.100 +    }
   1.101 +
   1.102 +    public static void main(String[] args) {
   1.103 +        boolean ok = new GenStubs().run(args);
   1.104 +        if (!ok)
   1.105 +            System.exit(1);
   1.106 +    }
   1.107 +
   1.108 +    boolean run(String... args) {
   1.109 +        File outdir = null;
   1.110 +        String sourcepath = null;
   1.111 +        List<String> classes = new ArrayList<String>();
   1.112 +        for (ListIterator<String> iter = Arrays.asList(args).listIterator(); iter.hasNext(); ) {
   1.113 +            String arg = iter.next();
   1.114 +            if (arg.equals("-s") && iter.hasNext())
   1.115 +                outdir = new File(iter.next());
   1.116 +            else if (arg.equals("-sourcepath") && iter.hasNext())
   1.117 +                sourcepath = iter.next();
   1.118 +            else if (arg.startsWith("-"))
   1.119 +                throw new IllegalArgumentException(arg);
   1.120 +            else {
   1.121 +                classes.add(arg);
   1.122 +                while (iter.hasNext())
   1.123 +                    classes.add(iter.next());
   1.124 +            }
   1.125 +        }
   1.126 +
   1.127 +        return run(sourcepath, outdir, classes);
   1.128 +    }
   1.129 +
   1.130 +    boolean run(String sourcepath, File outdir, List<String> classes) {
   1.131 +        //System.err.println("run: sourcepath:" + sourcepath + " outdir:" + outdir + " classes:" + classes);
   1.132 +        if (sourcepath == null)
   1.133 +            throw new IllegalArgumentException("sourcepath not set");
   1.134 +        if (outdir == null)
   1.135 +            throw new IllegalArgumentException("source output dir not set");
   1.136 +
   1.137 +        JavacTool tool = JavacTool.create();
   1.138 +        StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null);
   1.139 +
   1.140 +        try {
   1.141 +            fm.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(outdir));
   1.142 +            fm.setLocation(StandardLocation.SOURCE_PATH, splitPath(sourcepath));
   1.143 +            List<JavaFileObject> files = new ArrayList<JavaFileObject>();
   1.144 +            for (String c: classes) {
   1.145 +                JavaFileObject fo = fm.getJavaFileForInput(
   1.146 +                        StandardLocation.SOURCE_PATH, c, JavaFileObject.Kind.SOURCE);
   1.147 +                if (fo == null)
   1.148 +                    error("class not found: " + c);
   1.149 +                else
   1.150 +                    files.add(fo);
   1.151 +            }
   1.152 +
   1.153 +            JavacTask t = tool.getTask(null, fm, null, null, null, files);
   1.154 +            Iterable<? extends CompilationUnitTree> trees = t.parse();
   1.155 +            for (CompilationUnitTree tree: trees) {
   1.156 +                makeStub(fm, tree);
   1.157 +            }
   1.158 +        } catch (IOException e) {
   1.159 +            error("IO error " + e, e);
   1.160 +        }
   1.161 +
   1.162 +        return (errors == 0);
   1.163 +    }
   1.164 +
   1.165 +    void makeStub(StandardJavaFileManager fm, CompilationUnitTree tree) throws IOException {
   1.166 +        CompilationUnitTree tree2 = new StubMaker().translate(tree);
   1.167 +
   1.168 +        String className = fm.inferBinaryName(StandardLocation.SOURCE_PATH, tree.getSourceFile());
   1.169 +        JavaFileObject fo = fm.getJavaFileForOutput(StandardLocation.SOURCE_OUTPUT,
   1.170 +                className, JavaFileObject.Kind.SOURCE, null);
   1.171 +        // System.err.println("Writing " + className + " to " + fo.getName());
   1.172 +        Writer out = fo.openWriter();
   1.173 +        try {
   1.174 +            new Pretty(out, true).printExpr((JCTree) tree2);
   1.175 +        } finally {
   1.176 +            out.close();
   1.177 +        }
   1.178 +    }
   1.179 +
   1.180 +    List<File> splitPath(String path) {
   1.181 +        List<File> list = new ArrayList<File>();
   1.182 +        for (String p: path.split(File.pathSeparator)) {
   1.183 +            if (p.length() > 0)
   1.184 +                list.add(new File(p));
   1.185 +        }
   1.186 +        return list;
   1.187 +    }
   1.188 +
   1.189 +    void error(String message) {
   1.190 +        System.err.println(message);
   1.191 +        errors++;
   1.192 +    }
   1.193 +
   1.194 +    void error(String message, Throwable cause) {
   1.195 +        error(message);
   1.196 +    }
   1.197 +
   1.198 +    int errors;
   1.199 +
   1.200 +    class StubMaker extends TreeTranslator {
   1.201 +        CompilationUnitTree translate(CompilationUnitTree tree) {
   1.202 +            return super.translate((JCCompilationUnit) tree);
   1.203 +        }
   1.204 +
   1.205 +        /**
   1.206 +         * compilation units: remove javadoc comments
   1.207 +         * -- required, in order to remove @deprecated tags, since we
   1.208 +         * (separately) remove all annotations, including @Deprecated
   1.209 +         */
   1.210 +        public void visitTopLevel(JCCompilationUnit tree) {
   1.211 +            super.visitTopLevel(tree);
   1.212 +            tree.docComments = Collections.emptyMap();
   1.213 +        }
   1.214 +
   1.215 +        /**
   1.216 +         * methods: remove method bodies, make methods native
   1.217 +         */
   1.218 +        @Override
   1.219 +        public void visitMethodDef(JCMethodDecl tree) {
   1.220 +            tree.mods = translate(tree.mods);
   1.221 +            tree.restype = translate(tree.restype);
   1.222 +            tree.typarams = translateTypeParams(tree.typarams);
   1.223 +            tree.params = translateVarDefs(tree.params);
   1.224 +            tree.thrown = translate(tree.thrown);
   1.225 +            if (tree.restype != null && tree.body != null) {
   1.226 +                tree.mods.flags |= Flags.NATIVE;
   1.227 +                tree.body = null;
   1.228 +            }
   1.229 +            result = tree;
   1.230 +        }
   1.231 +
   1.232 +        /**
   1.233 +         * modifiers: remove annotations
   1.234 +         */
   1.235 +        @Override
   1.236 +        public void visitModifiers(JCModifiers tree) {
   1.237 +            tree.annotations = com.sun.tools.javac.util.List.nil();
   1.238 +            result = tree;
   1.239 +        }
   1.240 +
   1.241 +        /**
   1.242 +         * field definitions: replace initializers with 0, 0.0, false etc
   1.243 +         * when possible -- i.e. leave public, protected initializers alone
   1.244 +         */
   1.245 +        @Override
   1.246 +        public void visitVarDef(JCVariableDecl tree) {
   1.247 +            tree.mods = translate(tree.mods);
   1.248 +            tree.vartype = translate(tree.vartype);
   1.249 +            if (tree.init != null) {
   1.250 +                if ((tree.mods.flags & (Flags.PUBLIC | Flags.PROTECTED)) != 0)
   1.251 +                    tree.init = translate(tree.init);
   1.252 +                else {
   1.253 +                    String t = tree.vartype.toString();
   1.254 +                    if (t.equals("boolean"))
   1.255 +                        tree.init = new JCLiteral(TypeTags.BOOLEAN, 0) { };
   1.256 +                    else if (t.equals("byte"))
   1.257 +                        tree.init = new JCLiteral(TypeTags.BYTE, 0) { };
   1.258 +                    else if (t.equals("char"))
   1.259 +                        tree.init = new JCLiteral(TypeTags.CHAR, 0) { };
   1.260 +                    else if (t.equals("double"))
   1.261 +                        tree.init = new JCLiteral(TypeTags.DOUBLE, 0.d) { };
   1.262 +                    else if (t.equals("float"))
   1.263 +                        tree.init = new JCLiteral(TypeTags.FLOAT, 0.f) { };
   1.264 +                    else if (t.equals("int"))
   1.265 +                        tree.init = new JCLiteral(TypeTags.INT, 0) { };
   1.266 +                    else if (t.equals("long"))
   1.267 +                        tree.init = new JCLiteral(TypeTags.LONG, 0) { };
   1.268 +                    else if (t.equals("short"))
   1.269 +                        tree.init = new JCLiteral(TypeTags.SHORT, 0) { };
   1.270 +                    else
   1.271 +                        tree.init = new JCLiteral(TypeTags.BOT, null) { };
   1.272 +                }
   1.273 +            }
   1.274 +            result = tree;
   1.275 +        }
   1.276 +    }
   1.277 +
   1.278 +    //---------- Ant Invocation ------------------------------------------------
   1.279 +
   1.280 +    public static class Ant extends MatchingTask {
   1.281 +        private File srcDir;
   1.282 +        private File destDir;
   1.283 +        private boolean fork;
   1.284 +        private Path classpath;
   1.285 +        private String includes;
   1.286 +
   1.287 +        public void setSrcDir(File dir) {
   1.288 +            this.srcDir = dir;
   1.289 +        }
   1.290 +
   1.291 +        public void setDestDir(File dir) {
   1.292 +            this.destDir = dir;
   1.293 +        }
   1.294 +
   1.295 +        public void setFork(boolean v) {
   1.296 +            this.fork = v;
   1.297 +        }
   1.298 +
   1.299 +        public void setClasspath(Path cp) {
   1.300 +            if (classpath == null)
   1.301 +                classpath = cp;
   1.302 +            else
   1.303 +                classpath.append(cp);
   1.304 +        }
   1.305 +
   1.306 +        public Path createClasspath() {
   1.307 +            if (classpath == null) {
   1.308 +                classpath = new Path(getProject());
   1.309 +            }
   1.310 +            return classpath.createPath();
   1.311 +        }
   1.312 +
   1.313 +        public void setClasspathRef(Reference r) {
   1.314 +            createClasspath().setRefid(r);
   1.315 +        }
   1.316 +
   1.317 +        public void setIncludes(String includes) {
   1.318 +            super.setIncludes(includes);
   1.319 +            this.includes = includes;
   1.320 +        }
   1.321 +
   1.322 +        @Override
   1.323 +        public void execute() {
   1.324 +            if (includes != null && includes.trim().isEmpty())
   1.325 +                return;
   1.326 +
   1.327 +            DirectoryScanner s = getDirectoryScanner(srcDir);
   1.328 +            String[] files = s.getIncludedFiles();
   1.329 +//            System.err.println("Ant.execute: srcDir " + srcDir);
   1.330 +//            System.err.println("Ant.execute: destDir " + destDir);
   1.331 +//            System.err.println("Ant.execute: files " + Arrays.asList(files));
   1.332 +
   1.333 +            files = filter(srcDir, destDir, files);
   1.334 +            if (files.length == 0)
   1.335 +                return;
   1.336 +            System.out.println("Generating " + files.length + " stub files to " + destDir);
   1.337 +
   1.338 +            List<String> classNames = new ArrayList<String>();
   1.339 +            for (String file: files) {
   1.340 +                classNames.add(file.replaceAll(".java$", "").replace('/', '.'));
   1.341 +            }
   1.342 +
   1.343 +            if (!fork) {
   1.344 +                GenStubs m = new GenStubs();
   1.345 +                boolean ok = m.run(srcDir.getPath(), destDir, classNames);
   1.346 +                if (!ok)
   1.347 +                    throw new BuildException("genstubs failed");
   1.348 +            } else {
   1.349 +                List<String> cmd = new ArrayList<String>();
   1.350 +                String java_home = System.getProperty("java.home");
   1.351 +                cmd.add(new File(new File(java_home, "bin"), "java").getPath());
   1.352 +                if (classpath != null)
   1.353 +                    cmd.add("-Xbootclasspath/p:" + classpath);
   1.354 +                cmd.add(GenStubs.class.getName());
   1.355 +                cmd.add("-sourcepath");
   1.356 +                cmd.add(srcDir.getPath());
   1.357 +                cmd.add("-s");
   1.358 +                cmd.add(destDir.getPath());
   1.359 +                cmd.addAll(classNames);
   1.360 +                //System.err.println("GenStubs exec " + cmd);
   1.361 +                ProcessBuilder pb = new ProcessBuilder(cmd);
   1.362 +                pb.redirectErrorStream(true);
   1.363 +                try {
   1.364 +                    Process p = pb.start();
   1.365 +                    BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
   1.366 +                    try {
   1.367 +                        String line;
   1.368 +                        while ((line = in.readLine()) != null)
   1.369 +                            System.out.println(line);
   1.370 +                    } finally {
   1.371 +                        in.close();
   1.372 +                    }
   1.373 +                    int rc = p.waitFor();
   1.374 +                    if (rc != 0)
   1.375 +                        throw new BuildException("genstubs failed");
   1.376 +                } catch (IOException e) {
   1.377 +                    throw new BuildException("genstubs failed", e);
   1.378 +                } catch (InterruptedException e) {
   1.379 +                    throw new BuildException("genstubs failed", e);
   1.380 +                }
   1.381 +            }
   1.382 +        }
   1.383 +
   1.384 +        String[] filter(File srcDir, File destDir, String[] files) {
   1.385 +            List<String> results = new ArrayList<String>();
   1.386 +            for (String f: files) {
   1.387 +                long srcTime = new File(srcDir, f).lastModified();
   1.388 +                long destTime = new File(destDir, f).lastModified();
   1.389 +                if (srcTime > destTime)
   1.390 +                    results.add(f);
   1.391 +            }
   1.392 +            return results.toArray(new String[results.size()]);
   1.393 +        }
   1.394 +    }
   1.395 +}

mercurial