GenStubs.java revision 1304:9d47f4850714
1/* 2 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package genstubs; 27 28import java.io.*; 29import java.util.*; 30import javax.tools.JavaFileObject; 31import javax.tools.StandardJavaFileManager; 32import javax.tools.StandardLocation; 33 34import com.sun.source.tree.CompilationUnitTree; 35import com.sun.source.util.JavacTask; 36import com.sun.tools.javac.api.JavacTool; 37import com.sun.tools.javac.code.Flags; 38import com.sun.tools.javac.code.TypeTags; 39import com.sun.tools.javac.tree.JCTree; 40import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 41import com.sun.tools.javac.tree.JCTree.JCFieldAccess; 42import com.sun.tools.javac.tree.JCTree.JCIdent; 43import com.sun.tools.javac.tree.JCTree.JCImport; 44import com.sun.tools.javac.tree.JCTree.JCLiteral; 45import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 46import com.sun.tools.javac.tree.JCTree.JCModifiers; 47import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 48import com.sun.tools.javac.tree.Pretty; 49import com.sun.tools.javac.tree.TreeMaker; 50import com.sun.tools.javac.tree.TreeScanner; 51import com.sun.tools.javac.tree.TreeTranslator; 52import com.sun.tools.javac.util.Context; 53import com.sun.tools.javac.util.ListBuffer; 54import com.sun.tools.javac.util.Name; 55import javax.tools.JavaFileManager; 56 57/** 58 * Generate stub source files by removing implementation details from input files. 59 * 60 * This is a special purpose stub generator, specific to the needs of generating 61 * stub files for JDK 7 API that are needed to compile langtools files that depend 62 * on that API. The stub generator works by removing as much of the API source code 63 * as possible without affecting the public signature, in order to reduce the 64 * transitive closure of the API being referenced. The resulting stubs can be 65 * put on the langtools sourcepath with -implicit:none to compile the langtools 66 * files that depend on the JDK 7 API. 67 * 68 * Usage: 69 * genstubs -s <outdir> -sourcepath <path> <classnames> 70 * 71 * The specified class names are looked up on the sourcepath, and corresponding 72 * stubs are written to the source output directory. 73 * 74 * Classes are parsed into javac ASTs, then processed with a javac TreeTranslator 75 * to remove implementation details, and written out in the source output directory. 76 * Documentation comments and annotations are removed. Method bodies are removed 77 * and methods are marked native. Private and package-private field definitions 78 * have their initializers replace with 0, 0.0, false, null as appropriate. 79 */ 80 81public class GenStubs { 82 static class Fault extends Exception { 83 private static final long serialVersionUID = 0; 84 Fault(String message) { 85 super(message); 86 } 87 Fault(String message, Throwable cause) { 88 super(message); 89 initCause(cause); 90 } 91 } 92 93 public static void main(String[] args) { 94 boolean ok = new GenStubs().run(args); 95 if (!ok) 96 System.exit(1); 97 } 98 99 public boolean run(String... args) { 100 File outdir = null; 101 String sourcepath = null; 102 List<String> classes = new ArrayList<String>(); 103 for (ListIterator<String> iter = Arrays.asList(args).listIterator(); iter.hasNext(); ) { 104 String arg = iter.next(); 105 if (arg.equals("-s") && iter.hasNext()) 106 outdir = new File(iter.next()); 107 else if (arg.equals("-sourcepath") && iter.hasNext()) 108 sourcepath = iter.next(); 109 else if (arg.startsWith("-")) 110 throw new IllegalArgumentException(arg); 111 else { 112 classes.add(arg); 113 while (iter.hasNext()) 114 classes.add(iter.next()); 115 } 116 } 117 118 return run(sourcepath, outdir, classes); 119 } 120 121 public boolean run(String sourcepath, File outdir, List<String> classes) { 122 //System.err.println("run: sourcepath:" + sourcepath + " outdir:" + outdir + " classes:" + classes); 123 if (sourcepath == null) 124 throw new IllegalArgumentException("sourcepath not set"); 125 if (outdir == null) 126 throw new IllegalArgumentException("source output dir not set"); 127 128 JavacTool tool = JavacTool.create(); 129 StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null); 130 131 try { 132 fm.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(outdir)); 133 fm.setLocation(StandardLocation.SOURCE_PATH, splitPath(sourcepath)); 134 List<JavaFileObject> files = new ArrayList<JavaFileObject>(); 135 for (String c: classes) { 136 JavaFileObject fo = fm.getJavaFileForInput( 137 StandardLocation.SOURCE_PATH, c, JavaFileObject.Kind.SOURCE); 138 if (fo == null) 139 error("class not found: " + c); 140 else 141 files.add(fo); 142 } 143 144 JavacTask t = tool.getTask(null, fm, null, null, null, files); 145 Iterable<? extends CompilationUnitTree> trees = t.parse(); 146 for (CompilationUnitTree tree: trees) { 147 makeStub(fm, tree); 148 } 149 } catch (IOException e) { 150 error("IO error " + e, e); 151 } 152 153 return (errors == 0); 154 } 155 156 void makeStub(StandardJavaFileManager fm, CompilationUnitTree tree) throws IOException { 157 CompilationUnitTree tree2 = new StubMaker().translate(tree); 158 CompilationUnitTree tree3 = new ImportCleaner(fm).removeRedundantImports(tree2); 159 160 String className = fm.inferBinaryName(StandardLocation.SOURCE_PATH, tree.getSourceFile()); 161 JavaFileObject fo = fm.getJavaFileForOutput(StandardLocation.SOURCE_OUTPUT, 162 className, JavaFileObject.Kind.SOURCE, null); 163 // System.err.println("Writing " + className + " to " + fo.getName()); 164 Writer out = fo.openWriter(); 165 try { 166 new Pretty(out, true).printExpr((JCTree) tree3); 167 } finally { 168 out.close(); 169 } 170 } 171 172 List<File> splitPath(String path) { 173 List<File> list = new ArrayList<File>(); 174 for (String p: path.split(File.pathSeparator)) { 175 if (p.length() > 0) 176 list.add(new File(p)); 177 } 178 return list; 179 } 180 181 void error(String message) { 182 System.err.println(message); 183 errors++; 184 } 185 186 void error(String message, Throwable cause) { 187 error(message); 188 } 189 190 int errors; 191 192 class StubMaker extends TreeTranslator { 193 CompilationUnitTree translate(CompilationUnitTree tree) { 194 return super.translate((JCCompilationUnit) tree); 195 } 196 197 /** 198 * compilation units: remove javadoc comments 199 * -- required, in order to remove @deprecated tags, since we 200 * (separately) remove all annotations, including @Deprecated 201 */ 202 public void visitTopLevel(JCCompilationUnit tree) { 203 super.visitTopLevel(tree); 204 tree.docComments = null; 205 } 206 207 /** 208 * methods: remove method bodies, make methods native 209 */ 210 @Override 211 public void visitMethodDef(JCMethodDecl tree) { 212 tree.mods = translate(tree.mods); 213 tree.restype = translate(tree.restype); 214 tree.typarams = translateTypeParams(tree.typarams); 215 tree.params = translateVarDefs(tree.params); 216 tree.thrown = translate(tree.thrown); 217 if (tree.restype != null && tree.body != null) { 218 tree.mods.flags |= Flags.NATIVE; 219 tree.body = null; 220 } 221 result = tree; 222 } 223 224 /** 225 * modifiers: remove annotations 226 */ 227 @Override 228 public void visitModifiers(JCModifiers tree) { 229 tree.annotations = com.sun.tools.javac.util.List.nil(); 230 result = tree; 231 } 232 233 /** 234 * field definitions: replace initializers with 0, 0.0, false etc 235 * when possible -- i.e. leave public, protected initializers alone 236 */ 237 @Override 238 public void visitVarDef(JCVariableDecl tree) { 239 tree.mods = translate(tree.mods); 240 tree.vartype = translate(tree.vartype); 241 if (tree.init != null) { 242 if ((tree.mods.flags & (Flags.PUBLIC | Flags.PROTECTED)) != 0) 243 tree.init = translate(tree.init); 244 else { 245 String t = tree.vartype.toString(); 246 if (t.equals("boolean")) 247 tree.init = new JCLiteral(TypeTags.BOOLEAN, 0) { }; 248 else if (t.equals("byte")) 249 tree.init = new JCLiteral(TypeTags.BYTE, 0) { }; 250 else if (t.equals("char")) 251 tree.init = new JCLiteral(TypeTags.CHAR, 0) { }; 252 else if (t.equals("double")) 253 tree.init = new JCLiteral(TypeTags.DOUBLE, 0.d) { }; 254 else if (t.equals("float")) 255 tree.init = new JCLiteral(TypeTags.FLOAT, 0.f) { }; 256 else if (t.equals("int")) 257 tree.init = new JCLiteral(TypeTags.INT, 0) { }; 258 else if (t.equals("long")) 259 tree.init = new JCLiteral(TypeTags.LONG, 0) { }; 260 else if (t.equals("short")) 261 tree.init = new JCLiteral(TypeTags.SHORT, 0) { }; 262 else 263 tree.init = new JCLiteral(TypeTags.BOT, null) { }; 264 } 265 } 266 result = tree; 267 } 268 } 269 270 class ImportCleaner extends TreeScanner { 271 private Set<Name> names = new HashSet<Name>(); 272 private TreeMaker m; 273 274 ImportCleaner(JavaFileManager fm) { 275 // ImportCleaner itself doesn't require a filemanager, but instantiating 276 // a TreeMaker does, indirectly (via ClassReader, sigh) 277 Context c = new Context(); 278 c.put(JavaFileManager.class, fm); 279 m = TreeMaker.instance(c); 280 } 281 282 CompilationUnitTree removeRedundantImports(CompilationUnitTree t) { 283 JCCompilationUnit tree = (JCCompilationUnit) t; 284 tree.accept(this); 285 ListBuffer<JCTree> defs = new ListBuffer<JCTree>(); 286 for (JCTree def: tree.defs) { 287 if (def.getTag() == JCTree.Tag.IMPORT) { 288 JCImport imp = (JCImport) def; 289 if (imp.qualid.getTag() == JCTree.Tag.SELECT) { 290 JCFieldAccess qualid = (JCFieldAccess) imp.qualid; 291 if (!qualid.name.toString().equals("*") 292 && !names.contains(qualid.name)) { 293 continue; 294 } 295 } 296 } 297 defs.add(def); 298 } 299 return m.TopLevel(tree.packageAnnotations, tree.pid, defs.toList()); 300 } 301 302 @Override 303 public void visitImport(JCImport tree) { } // ignore names found in imports 304 305 @Override 306 public void visitIdent(JCIdent tree) { 307 names.add(tree.name); 308 } 309 310 @Override 311 public void visitSelect(JCFieldAccess tree) { 312 super.visitSelect(tree); 313 names.add(tree.name); 314 } 315 } 316} 317