Thu, 04 Feb 2010 10:14:28 -0800
6923080: TreeScanner.visitNewClass should scan tree.typeargs
Reviewed-by: darcy
1 /*
2 * Copyright 2004-2009 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
26 package com.sun.tools.apt.comp;
28 import com.sun.tools.javac.code.*;
29 import com.sun.tools.javac.comp.*;
30 import com.sun.tools.javac.tree.*;
31 import com.sun.tools.javac.util.*;
32 import com.sun.tools.javac.tree.TreeScanner;
33 import com.sun.tools.javac.util.Context;
34 import com.sun.tools.apt.util.Bark;
35 import com.sun.tools.javac.util.Position;
37 import java.util.*;
38 import java.util.regex.*;
39 import java.lang.reflect.*;
40 import java.lang.reflect.InvocationTargetException;
41 import java.io.IOException;
43 import com.sun.tools.apt.*;
44 import com.sun.tools.apt.comp.*;
45 import com.sun.tools.javac.code.Symbol.*;
47 import com.sun.mirror.declaration.TypeDeclaration;
48 import com.sun.mirror.declaration.AnnotationTypeDeclaration;
49 import com.sun.mirror.apt.*;
50 // import com.sun.mirror.apt.AnnotationProcessorFactory;
51 import com.sun.mirror.apt.AnnotationProcessors;
53 import com.sun.tools.apt.mirror.AptEnv;
54 import com.sun.tools.apt.mirror.apt.FilerImpl;
55 import com.sun.tools.apt.mirror.apt.AnnotationProcessorEnvironmentImpl;
58 import static com.sun.tools.apt.mirror.declaration.DeclarationMaker.isJavaIdentifier;
60 /**
61 * Apt compiler phase.
62 *
63 * <p><b>This is NOT part of any API supported by Sun Microsystems.
64 * If you write code that depends on this, you do so at your own
65 * risk. This code and its internal interfaces are subject to change
66 * or deletion without notice.</b>
67 */
68 @SuppressWarnings("deprecation")
69 public class Apt extends ListBuffer<Env<AttrContext>> {
70 java.util.Set<String> genSourceFileNames = new java.util.LinkedHashSet<String>();
71 public java.util.Set<String> getSourceFileNames() {
72 return genSourceFileNames;
73 }
75 /** List of names of generated class files.
76 */
77 java.util.Set<String> genClassFileNames = new java.util.LinkedHashSet<String>();
78 public java.util.Set<String> getClassFileNames() {
79 return genClassFileNames;
80 }
82 /* AptEnvironment */
83 AptEnv aptenv;
85 private Context context;
87 /** The context key for the todo list. */
89 protected static final Context.Key<Apt> aptKey =
90 new Context.Key<Apt>();
92 /** Get the Apt instance for this context. */
93 public static Apt instance(Context context) {
94 Apt instance = context.get(aptKey);
95 if (instance == null)
96 instance = new Apt(context);
97 return instance;
98 }
100 /** Create a new apt list. */
101 protected Apt(Context context) {
102 this.context = context;
104 context.put(aptKey, this);
105 aptenv = AptEnv.instance(context);
106 }
108 /**
109 * Used to scan javac trees to build data structures needed for
110 * bootstrapping the apt environment. In particular:
111 *
112 * <ul>
113 *
114 * <li> Generate list of canonical names of annotation types that
115 * appear in source files given on the command line
116 *
117 * <li> Collect list of javac symbols representing source files
118 * given on the command line
119 *
120 * </ul>
121 */
122 static class AptTreeScanner extends TreeScanner {
124 // Set of fully qualified names of annotation types present in
125 // examined source
126 private Set<String> annotationSet;
128 // Symbols to build bootstrapping declaration list
129 private Collection<ClassSymbol> specifiedDeclCollection;
130 private Collection<ClassSymbol> declCollection;
132 public Set<String> getAnnotationSet() {
133 return annotationSet;
134 }
136 public AptTreeScanner() {
137 annotationSet = new LinkedHashSet<String>();
138 specifiedDeclCollection = new LinkedHashSet<ClassSymbol>();
139 declCollection = new LinkedHashSet<ClassSymbol>();
140 }
142 public void visitTopLevel(JCTree.JCCompilationUnit tree) {
143 super.visitTopLevel(tree);
144 // Print out contents -- what are we dealing with?
146 for(JCTree d: tree.defs) {
147 if (d instanceof JCTree.JCClassDecl)
148 specifiedDeclCollection.add(((JCTree.JCClassDecl) d).sym);
149 }
151 }
153 public void visitBlock(JCTree.JCBlock tree) {
154 ; // Do nothing.
155 }
158 // should add nested classes to packages, etc.
159 public void visitClassDef(JCTree.JCClassDecl tree) {
160 if (tree.sym == null) {
161 // could be an anon class w/in an initializer
162 return;
163 }
165 super.visitClassDef(tree);
167 declCollection.add(tree.sym);
168 }
170 public void visitMethodDef(JCTree.JCMethodDecl tree) {
171 super.visitMethodDef(tree);
172 }
174 public void visitVarDef(JCTree.JCVariableDecl tree) {
175 super.visitVarDef(tree);
176 }
178 public void visitAnnotation(JCTree.JCAnnotation tree) {
179 super.visitAnnotation(tree);
180 annotationSet.add(tree.type.tsym.toString());
181 }
182 }
184 Set<String> computeAnnotationSet(Collection<ClassSymbol> classSymbols) {
185 Set<String> annotationSet = new HashSet<String>();
187 for(ClassSymbol classSymbol: classSymbols) {
188 computeAnnotationSet(classSymbol, annotationSet);
189 }
190 return annotationSet;
191 }
193 void computeAnnotationSet(Symbol symbol, Set<String> annotationSet) {
194 if (symbol != null ) {
195 if (symbol.getAnnotationMirrors() != null)
196 for(Attribute.Compound compound: symbol.getAnnotationMirrors())
197 annotationSet.add(compound.type.tsym.toString()); // should fullName be used instead of toString?
199 if (symbol instanceof Symbol.MethodSymbol) // add parameter annotations
200 for(Symbol param: ((MethodSymbol) symbol).params())
201 computeAnnotationSet(param, annotationSet);
203 if (symbol.members() != null) {
204 for(Scope.Entry e = symbol.members().elems; e != null; e = e.sibling)
205 computeAnnotationSet(e.sym, annotationSet);
206 }
207 }
208 }
210 public void main(com.sun.tools.javac.util.List<JCTree.JCCompilationUnit> treeList,
211 ListBuffer<ClassSymbol> classes,
212 Map<String, String> origOptions,
213 ClassLoader aptCL,
214 AnnotationProcessorFactory providedFactory,
215 java.util.Set<Class<? extends AnnotationProcessorFactory> > productiveFactories) {
216 Bark bark = Bark.instance(context);
217 java.io.PrintWriter out = bark.warnWriter;
218 Options options = Options.instance(context);
220 Collection<TypeDeclaration> spectypedecls = new LinkedHashSet<TypeDeclaration>();
221 Collection<TypeDeclaration> typedecls = new LinkedHashSet<TypeDeclaration>();
222 Set<String> unmatchedAnnotations = new LinkedHashSet<String>();
223 Set<AnnotationTypeDeclaration> emptyATDS = Collections.emptySet();
224 Set<Class<? extends AnnotationProcessorFactory> > currentRoundFactories =
225 new LinkedHashSet<Class<? extends AnnotationProcessorFactory> >();
227 // Determine what annotations are present on the input source
228 // files, create collections of specified type declarations,
229 // and type declarations.
230 AptTreeScanner ats = new AptTreeScanner();
231 for(JCTree t: treeList) {
232 t.accept(ats);
233 }
235 // Turn collection of ClassSymbols into Collection of apt decls
236 for (ClassSymbol cs : ats.specifiedDeclCollection) {
237 TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs);
238 spectypedecls.add(decl);
239 }
241 for (ClassSymbol cs : ats.declCollection) {
242 TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs);
243 typedecls.add(decl);
244 }
246 unmatchedAnnotations.addAll(ats.getAnnotationSet());
248 // Process input class files
249 for(ClassSymbol cs : classes) {
250 TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs);
251 // System.out.println("Adding a class to spectypedecls");
252 spectypedecls.add(decl);
253 typedecls.add(decl);
254 computeAnnotationSet(cs, unmatchedAnnotations);
255 }
257 if (options.get("-XListAnnotationTypes") != null) {
258 out.println("Set of annotations found:" +
259 (new TreeSet<String>(unmatchedAnnotations)).toString());
260 }
262 AnnotationProcessorEnvironmentImpl trivAPE =
263 new AnnotationProcessorEnvironmentImpl(spectypedecls, typedecls, origOptions, context);
265 if (options.get("-XListDeclarations") != null) {
266 out.println("Set of Specified Declarations:" +
267 spectypedecls);
269 out.println("Set of Included Declarations: " +
270 typedecls);
271 }
273 if (options.get("-print") != null) {
274 if (spectypedecls.size() == 0 )
275 throw new UsageMessageNeededException();
277 // Run the printing processor
278 AnnotationProcessor proc = (new BootstrapAPF()).getProcessorFor(new HashSet<AnnotationTypeDeclaration>(),
279 trivAPE);
280 proc.process();
281 } else {
282 // Discovery process
284 // List of annotation processory factory instances
285 java.util.Iterator<AnnotationProcessorFactory> providers = null;
286 {
287 /*
288 * If a factory is provided by the user, the
289 * "-factory" and "-factorypath" options are not used.
290 *
291 * Otherwise, if the "-factory" option is used, search
292 * the appropriate path for the named class.
293 * Otherwise, use sun.misc.Service to implement the
294 * default discovery policy.
295 */
297 java.util.List<AnnotationProcessorFactory> list =
298 new LinkedList<AnnotationProcessorFactory>();
299 String factoryName = options.get("-factory");
301 if (providedFactory != null) {
302 list.add(providedFactory);
303 providers = list.iterator();
304 } else if (factoryName != null) {
305 try {
306 AnnotationProcessorFactory factory =
307 (AnnotationProcessorFactory) (aptCL.loadClass(factoryName).newInstance());
308 list.add(factory);
309 } catch (ClassNotFoundException cnfe) {
310 bark.aptWarning("FactoryNotFound", factoryName);
311 } catch (ClassCastException cce) {
312 bark.aptWarning("FactoryWrongType", factoryName);
313 } catch (Exception e ) {
314 bark.aptWarning("FactoryCantInstantiate", factoryName);
315 } catch(Throwable t) {
316 throw new AnnotationProcessingError(t);
317 }
319 providers = list.iterator();
320 } else {
321 @SuppressWarnings("unchecked")
322 Iterator<AnnotationProcessorFactory> iter =
323 sun.misc.Service.providers(AnnotationProcessorFactory.class, aptCL);
324 providers = iter;
326 }
327 }
329 java.util.Map<AnnotationProcessorFactory, Set<AnnotationTypeDeclaration>> factoryToAnnotation =
330 new LinkedHashMap<AnnotationProcessorFactory, Set<AnnotationTypeDeclaration>>();
332 if (!providers.hasNext() && productiveFactories.size() == 0) {
333 if (unmatchedAnnotations.size() > 0)
334 bark.aptWarning("NoAnnotationProcessors");
335 if (spectypedecls.size() == 0)
336 throw new UsageMessageNeededException();
337 return; // no processors; nothing else to do
338 } else {
339 // If there are no annotations, still give
340 // processors that match everything a chance to
341 // run.
343 if(unmatchedAnnotations.size() == 0)
344 unmatchedAnnotations.add("");
346 Set<String> emptyStringSet = new HashSet<String>();
347 emptyStringSet.add("");
348 emptyStringSet = Collections.unmodifiableSet(emptyStringSet);
350 while (providers.hasNext() ) {
351 Object provider = providers.next();
352 try {
353 Set<String> matchedStrings = new HashSet<String>();
355 AnnotationProcessorFactory apf = (AnnotationProcessorFactory) provider;
356 Collection<String> supportedTypes = apf.supportedAnnotationTypes();
358 Collection<Pattern> supportedTypePatterns = new LinkedList<Pattern>();
359 for(String s: supportedTypes)
360 supportedTypePatterns.add(importStringToPattern(s));
362 for(String s: unmatchedAnnotations) {
363 for(Pattern p: supportedTypePatterns) {
364 if (p.matcher(s).matches()) {
365 matchedStrings.add(s);
366 break;
367 }
368 }
369 }
371 unmatchedAnnotations.removeAll(matchedStrings);
373 if (options.get("-XPrintFactoryInfo") != null) {
374 out.println("Factory " + apf.getClass().getName() +
375 " matches " +
376 ((matchedStrings.size() == 0)?
377 "nothing.": matchedStrings));
378 }
380 if (matchedStrings.size() > 0) {
381 // convert annotation names to annotation
382 // type decls
383 Set<AnnotationTypeDeclaration> atds = new HashSet<AnnotationTypeDeclaration>();
385 // If a "*" processor is called on the
386 // empty string, pass in an empty set of
387 // annotation type declarations.
388 if (!matchedStrings.equals(emptyStringSet)) {
389 for(String s: matchedStrings) {
390 TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(s);
391 AnnotationTypeDeclaration annotdecl;
392 if (decl == null) {
393 bark.aptError("DeclarationCreation", s);
394 } else {
395 try {
396 annotdecl = (AnnotationTypeDeclaration)decl;
397 atds.add(annotdecl);
399 } catch (ClassCastException cce) {
400 bark.aptError("BadDeclaration", s);
401 }
402 }
403 }
404 }
406 currentRoundFactories.add(apf.getClass());
407 productiveFactories.add(apf.getClass());
408 factoryToAnnotation.put(apf, atds);
409 } else if (productiveFactories.contains(apf.getClass())) {
410 // If a factory provided a processor in a
411 // previous round but doesn't match any
412 // annotations this round, call it with an
413 // empty set of declarations.
414 currentRoundFactories.add(apf.getClass());
415 factoryToAnnotation.put(apf, emptyATDS );
416 }
418 if (unmatchedAnnotations.size() == 0)
419 break;
421 } catch (ClassCastException cce) {
422 bark.aptWarning("BadFactory", cce);
423 }
424 }
426 unmatchedAnnotations.remove("");
427 }
429 // If the set difference of productiveFactories and
430 // currentRoundFactories is non-empty, call the remaining
431 // productive factories with an empty set of declarations.
432 {
433 java.util.Set<Class<? extends AnnotationProcessorFactory> > neglectedFactories =
434 new LinkedHashSet<Class<? extends AnnotationProcessorFactory>>(productiveFactories);
435 neglectedFactories.removeAll(currentRoundFactories);
436 for(Class<? extends AnnotationProcessorFactory> working : neglectedFactories) {
437 try {
438 AnnotationProcessorFactory factory = working.newInstance();
439 factoryToAnnotation.put(factory, emptyATDS);
440 } catch (Exception e ) {
441 bark.aptWarning("FactoryCantInstantiate", working.getName());
442 } catch(Throwable t) {
443 throw new AnnotationProcessingError(t);
444 }
445 }
446 }
448 if (unmatchedAnnotations.size() > 0)
449 bark.aptWarning("AnnotationsWithoutProcessors", unmatchedAnnotations);
451 Set<AnnotationProcessor> processors = new LinkedHashSet<AnnotationProcessor>();
453 // If there were no source files AND no factory matching "*",
454 // make sure the usage message is printed
455 if (spectypedecls.size() == 0 &&
456 factoryToAnnotation.keySet().size() == 0 )
457 throw new UsageMessageNeededException();
459 try {
460 for(AnnotationProcessorFactory apFactory: factoryToAnnotation.keySet()) {
461 AnnotationProcessor processor = apFactory.getProcessorFor(factoryToAnnotation.get(apFactory),
462 trivAPE);
463 if (processor != null)
464 processors.add(processor);
465 else
466 bark.aptWarning("NullProcessor", apFactory.getClass().getName());
467 }
468 } catch(Throwable t) {
469 throw new AnnotationProcessingError(t);
470 }
472 LinkedList<AnnotationProcessor> temp = new LinkedList<AnnotationProcessor>();
473 temp.addAll(processors);
475 AnnotationProcessor proc = AnnotationProcessors.getCompositeAnnotationProcessor(temp);
477 try {
478 proc.process();
479 } catch (Throwable t) {
480 throw new AnnotationProcessingError(t);
481 }
483 // Invoke listener callback mechanism
484 trivAPE.roundComplete();
486 FilerImpl filerimpl = (FilerImpl)trivAPE.getFiler();
487 genSourceFileNames = filerimpl.getSourceFileNames();
488 genClassFileNames = filerimpl.getClassFileNames();
489 filerimpl.flush(); // Make sure new files are written out
490 }
491 }
493 /**
494 * Convert import-style string to regex matching that string. If
495 * the string is a valid import-style string, return a regex that
496 * won't match anything.
497 */
498 Pattern importStringToPattern(String s) {
499 if (s.equals("*")) {
500 return allMatches;
501 } else {
502 String t = s;
503 boolean star = false;
505 /*
506 * Validate string from factory is legal. If the string
507 * has more than one asterisks or the asterisks does not
508 * appear as the last character (preceded by a period),
509 * the string is not legal.
510 */
512 boolean valid = true;
513 int index = t.indexOf('*');
514 if (index != -1) {
515 // '*' must be last character...
516 if (index == t.length() -1) {
517 // ... and preceeding character must be '.'
518 if ( index-1 >= 0 ) {
519 valid = t.charAt(index-1) == '.';
520 // Strip off ".*$" for identifier checks
521 t = t.substring(0, t.length()-2);
522 }
523 } else
524 valid = false;
525 }
527 // Verify string is off the form (javaId \.)+ or javaId
528 if (valid) {
529 String[] javaIds = t.split("\\.", t.length()+2);
530 for(String javaId: javaIds)
531 valid &= isJavaIdentifier(javaId);
532 }
534 if (!valid) {
535 Bark bark = Bark.instance(context);
536 bark.aptWarning("MalformedSupportedString", s);
537 return noMatches; // won't match any valid identifier
538 }
540 String s_prime = s.replaceAll("\\.", "\\\\.");
542 if (s_prime.endsWith("*")) {
543 s_prime = s_prime.substring(0, s_prime.length() - 1) + ".+";
544 }
546 return Pattern.compile(s_prime);
547 }
548 }
550 private static final Pattern allMatches = Pattern.compile(".*");
551 private static final Pattern noMatches = Pattern.compile("(\\P{all})+");
552 }