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 +}