|
1 /* |
|
2 * Copyright (c) 1999, 2016, 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 |
|
26 package com.sun.tools.javac.code; |
|
27 |
|
28 import java.io.IOException; |
|
29 import java.nio.file.Path; |
|
30 import java.util.EnumSet; |
|
31 import java.util.HashMap; |
|
32 import java.util.Iterator; |
|
33 import java.util.Map; |
|
34 import java.util.NoSuchElementException; |
|
35 import java.util.Set; |
|
36 |
|
37 import javax.lang.model.SourceVersion; |
|
38 import javax.tools.JavaFileManager; |
|
39 import javax.tools.JavaFileManager.Location; |
|
40 import javax.tools.JavaFileObject; |
|
41 import javax.tools.JavaFileObject.Kind; |
|
42 import javax.tools.StandardJavaFileManager; |
|
43 import javax.tools.StandardLocation; |
|
44 |
|
45 import com.sun.tools.javac.code.Scope.WriteableScope; |
|
46 import com.sun.tools.javac.code.Symbol.ClassSymbol; |
|
47 import com.sun.tools.javac.code.Symbol.Completer; |
|
48 import com.sun.tools.javac.code.Symbol.CompletionFailure; |
|
49 import com.sun.tools.javac.code.Symbol.ModuleSymbol; |
|
50 import com.sun.tools.javac.code.Symbol.PackageSymbol; |
|
51 import com.sun.tools.javac.code.Symbol.TypeSymbol; |
|
52 import com.sun.tools.javac.comp.Annotate; |
|
53 import com.sun.tools.javac.file.JRTIndex; |
|
54 import com.sun.tools.javac.file.JavacFileManager; |
|
55 import com.sun.tools.javac.jvm.ClassReader; |
|
56 import com.sun.tools.javac.jvm.Profile; |
|
57 import com.sun.tools.javac.main.Option; |
|
58 import com.sun.tools.javac.platform.PlatformDescription; |
|
59 import com.sun.tools.javac.resources.CompilerProperties.Fragments; |
|
60 import com.sun.tools.javac.util.*; |
|
61 |
|
62 import static javax.tools.StandardLocation.*; |
|
63 |
|
64 import static com.sun.tools.javac.code.Flags.*; |
|
65 import static com.sun.tools.javac.code.Kinds.Kind.*; |
|
66 |
|
67 import com.sun.tools.javac.util.Dependencies.CompletionCause; |
|
68 |
|
69 /** |
|
70 * This class provides operations to locate class definitions |
|
71 * from the source and class files on the paths provided to javac. |
|
72 * |
|
73 * <p><b>This is NOT part of any supported API. |
|
74 * If you write code that depends on this, you do so at your own risk. |
|
75 * This code and its internal interfaces are subject to change or |
|
76 * deletion without notice.</b> |
|
77 */ |
|
78 public class ClassFinder { |
|
79 /** The context key for the class finder. */ |
|
80 protected static final Context.Key<ClassFinder> classFinderKey = new Context.Key<>(); |
|
81 |
|
82 ClassReader reader; |
|
83 |
|
84 private final Annotate annotate; |
|
85 |
|
86 /** Switch: verbose output. |
|
87 */ |
|
88 boolean verbose; |
|
89 |
|
90 /** |
|
91 * Switch: cache completion failures unless -XDdev is used |
|
92 */ |
|
93 private boolean cacheCompletionFailure; |
|
94 |
|
95 /** |
|
96 * Switch: prefer source files instead of newer when both source |
|
97 * and class are available |
|
98 **/ |
|
99 protected boolean preferSource; |
|
100 |
|
101 /** |
|
102 * Switch: Search classpath and sourcepath for classes before the |
|
103 * bootclasspath |
|
104 */ |
|
105 protected boolean userPathsFirst; |
|
106 |
|
107 /** |
|
108 * Switch: should read OTHER classfiles (.sig files) from PLATFORM_CLASS_PATH. |
|
109 */ |
|
110 private boolean allowSigFiles; |
|
111 |
|
112 /** The log to use for verbose output |
|
113 */ |
|
114 final Log log; |
|
115 |
|
116 /** The symbol table. */ |
|
117 Symtab syms; |
|
118 |
|
119 /** The name table. */ |
|
120 final Names names; |
|
121 |
|
122 /** Force a completion failure on this name |
|
123 */ |
|
124 final Name completionFailureName; |
|
125 |
|
126 /** Access to files |
|
127 */ |
|
128 private final JavaFileManager fileManager; |
|
129 |
|
130 /** Dependency tracker |
|
131 */ |
|
132 private final Dependencies dependencies; |
|
133 |
|
134 /** Factory for diagnostics |
|
135 */ |
|
136 JCDiagnostic.Factory diagFactory; |
|
137 |
|
138 /** Can be reassigned from outside: |
|
139 * the completer to be used for ".java" files. If this remains unassigned |
|
140 * ".java" files will not be loaded. |
|
141 */ |
|
142 public Completer sourceCompleter = Completer.NULL_COMPLETER; |
|
143 |
|
144 /** The path name of the class file currently being read. |
|
145 */ |
|
146 protected JavaFileObject currentClassFile = null; |
|
147 |
|
148 /** The class or method currently being read. |
|
149 */ |
|
150 protected Symbol currentOwner = null; |
|
151 |
|
152 /** |
|
153 * The currently selected profile. |
|
154 */ |
|
155 private final Profile profile; |
|
156 |
|
157 /** |
|
158 * Use direct access to the JRTIndex to access the temporary |
|
159 * replacement for the info that used to be in ct.sym. |
|
160 * In time, this will go away and be replaced by the module system. |
|
161 */ |
|
162 private final JRTIndex jrtIndex; |
|
163 |
|
164 /** |
|
165 * Completer that delegates to the complete-method of this class. |
|
166 */ |
|
167 private final Completer thisCompleter = this::complete; |
|
168 |
|
169 public Completer getCompleter() { |
|
170 return thisCompleter; |
|
171 } |
|
172 |
|
173 /** Get the ClassFinder instance for this invocation. */ |
|
174 public static ClassFinder instance(Context context) { |
|
175 ClassFinder instance = context.get(classFinderKey); |
|
176 if (instance == null) |
|
177 instance = new ClassFinder(context); |
|
178 return instance; |
|
179 } |
|
180 |
|
181 /** Construct a new class finder. */ |
|
182 protected ClassFinder(Context context) { |
|
183 context.put(classFinderKey, this); |
|
184 reader = ClassReader.instance(context); |
|
185 names = Names.instance(context); |
|
186 syms = Symtab.instance(context); |
|
187 fileManager = context.get(JavaFileManager.class); |
|
188 dependencies = Dependencies.instance(context); |
|
189 if (fileManager == null) |
|
190 throw new AssertionError("FileManager initialization error"); |
|
191 diagFactory = JCDiagnostic.Factory.instance(context); |
|
192 |
|
193 log = Log.instance(context); |
|
194 annotate = Annotate.instance(context); |
|
195 |
|
196 Options options = Options.instance(context); |
|
197 verbose = options.isSet(Option.VERBOSE); |
|
198 cacheCompletionFailure = options.isUnset("dev"); |
|
199 preferSource = "source".equals(options.get("-Xprefer")); |
|
200 userPathsFirst = options.isSet(Option.XXUSERPATHSFIRST); |
|
201 allowSigFiles = context.get(PlatformDescription.class) != null; |
|
202 |
|
203 completionFailureName = |
|
204 options.isSet("failcomplete") |
|
205 ? names.fromString(options.get("failcomplete")) |
|
206 : null; |
|
207 |
|
208 // Temporary, until more info is available from the module system. |
|
209 boolean useCtProps; |
|
210 JavaFileManager fm = context.get(JavaFileManager.class); |
|
211 if (fm instanceof JavacFileManager) { |
|
212 JavacFileManager jfm = (JavacFileManager) fm; |
|
213 useCtProps = jfm.isDefaultBootClassPath() && jfm.isSymbolFileEnabled(); |
|
214 } else if (fm.getClass().getName().equals("com.sun.tools.sjavac.comp.SmartFileManager")) { |
|
215 useCtProps = !options.isSet("ignore.symbol.file"); |
|
216 } else { |
|
217 useCtProps = false; |
|
218 } |
|
219 jrtIndex = useCtProps && JRTIndex.isAvailable() ? JRTIndex.getSharedInstance() : null; |
|
220 |
|
221 profile = Profile.instance(context); |
|
222 } |
|
223 |
|
224 |
|
225 /************************************************************************ |
|
226 * Temporary ct.sym replacement |
|
227 * |
|
228 * The following code is a temporary substitute for the ct.sym mechanism |
|
229 * used in JDK 6 thru JDK 8. |
|
230 * This mechanism will eventually be superseded by the Jigsaw module system. |
|
231 ***********************************************************************/ |
|
232 |
|
233 /** |
|
234 * Returns any extra flags for a class symbol. |
|
235 * This information used to be provided using private annotations |
|
236 * in the class file in ct.sym; in time, this information will be |
|
237 * available from the module system. |
|
238 */ |
|
239 long getSupplementaryFlags(ClassSymbol c) { |
|
240 if (jrtIndex == null || !jrtIndex.isInJRT(c.classfile) || c.name == names.module_info) { |
|
241 return 0; |
|
242 } |
|
243 |
|
244 if (supplementaryFlags == null) { |
|
245 supplementaryFlags = new HashMap<>(); |
|
246 } |
|
247 |
|
248 Long flags = supplementaryFlags.get(c.packge()); |
|
249 if (flags == null) { |
|
250 long newFlags = 0; |
|
251 try { |
|
252 JRTIndex.CtSym ctSym = jrtIndex.getCtSym(c.packge().flatName()); |
|
253 Profile minProfile = Profile.DEFAULT; |
|
254 if (ctSym.proprietary) |
|
255 newFlags |= PROPRIETARY; |
|
256 if (ctSym.minProfile != null) |
|
257 minProfile = Profile.lookup(ctSym.minProfile); |
|
258 if (profile != Profile.DEFAULT && minProfile.value > profile.value) { |
|
259 newFlags |= NOT_IN_PROFILE; |
|
260 } |
|
261 } catch (IOException ignore) { |
|
262 } |
|
263 supplementaryFlags.put(c.packge(), flags = newFlags); |
|
264 } |
|
265 return flags; |
|
266 } |
|
267 |
|
268 private Map<PackageSymbol, Long> supplementaryFlags; |
|
269 |
|
270 /************************************************************************ |
|
271 * Loading Classes |
|
272 ***********************************************************************/ |
|
273 |
|
274 /** Completion for classes to be loaded. Before a class is loaded |
|
275 * we make sure its enclosing class (if any) is loaded. |
|
276 */ |
|
277 private void complete(Symbol sym) throws CompletionFailure { |
|
278 if (sym.kind == TYP) { |
|
279 try { |
|
280 ClassSymbol c = (ClassSymbol) sym; |
|
281 dependencies.push(c, CompletionCause.CLASS_READER); |
|
282 annotate.blockAnnotations(); |
|
283 c.members_field = new Scope.ErrorScope(c); // make sure it's always defined |
|
284 completeOwners(c.owner); |
|
285 completeEnclosing(c); |
|
286 fillIn(c); |
|
287 } finally { |
|
288 annotate.unblockAnnotationsNoFlush(); |
|
289 dependencies.pop(); |
|
290 } |
|
291 } else if (sym.kind == PCK) { |
|
292 PackageSymbol p = (PackageSymbol)sym; |
|
293 try { |
|
294 fillIn(p); |
|
295 } catch (IOException ex) { |
|
296 JCDiagnostic msg = |
|
297 diagFactory.fragment(Fragments.ExceptionMessage(ex.getLocalizedMessage())); |
|
298 throw new CompletionFailure(sym, msg).initCause(ex); |
|
299 } |
|
300 } |
|
301 if (!reader.filling) |
|
302 annotate.flush(); // finish attaching annotations |
|
303 } |
|
304 |
|
305 /** complete up through the enclosing package. */ |
|
306 private void completeOwners(Symbol o) { |
|
307 if (o.kind != PCK) completeOwners(o.owner); |
|
308 o.complete(); |
|
309 } |
|
310 |
|
311 /** |
|
312 * Tries to complete lexically enclosing classes if c looks like a |
|
313 * nested class. This is similar to completeOwners but handles |
|
314 * the situation when a nested class is accessed directly as it is |
|
315 * possible with the Tree API or javax.lang.model.*. |
|
316 */ |
|
317 private void completeEnclosing(ClassSymbol c) { |
|
318 if (c.owner.kind == PCK) { |
|
319 Symbol owner = c.owner; |
|
320 for (Name name : Convert.enclosingCandidates(Convert.shortName(c.name))) { |
|
321 Symbol encl = owner.members().findFirst(name); |
|
322 if (encl == null) |
|
323 encl = syms.getClass(c.packge().modle, TypeSymbol.formFlatName(name, owner)); |
|
324 if (encl != null) |
|
325 encl.complete(); |
|
326 } |
|
327 } |
|
328 } |
|
329 |
|
330 /** Fill in definition of class `c' from corresponding class or |
|
331 * source file. |
|
332 */ |
|
333 void fillIn(ClassSymbol c) { |
|
334 if (completionFailureName == c.fullname) { |
|
335 JCDiagnostic msg = |
|
336 diagFactory.fragment(Fragments.UserSelectedCompletionFailure); |
|
337 throw new CompletionFailure(c, msg); |
|
338 } |
|
339 currentOwner = c; |
|
340 JavaFileObject classfile = c.classfile; |
|
341 if (classfile != null) { |
|
342 JavaFileObject previousClassFile = currentClassFile; |
|
343 Symbol prevOwner = c.owner; |
|
344 Name prevName = c.fullname; |
|
345 try { |
|
346 if (reader.filling) { |
|
347 Assert.error("Filling " + classfile.toUri() + " during " + previousClassFile); |
|
348 } |
|
349 currentClassFile = classfile; |
|
350 if (verbose) { |
|
351 log.printVerbose("loading", currentClassFile.getName()); |
|
352 } |
|
353 if (classfile.getKind() == JavaFileObject.Kind.CLASS || |
|
354 classfile.getKind() == JavaFileObject.Kind.OTHER) { |
|
355 reader.readClassFile(c); |
|
356 c.flags_field |= getSupplementaryFlags(c); |
|
357 } else { |
|
358 if (!sourceCompleter.isTerminal()) { |
|
359 sourceCompleter.complete(c); |
|
360 } else { |
|
361 throw new IllegalStateException("Source completer required to read " |
|
362 + classfile.toUri()); |
|
363 } |
|
364 } |
|
365 } catch (BadClassFile cf) { |
|
366 //the symbol may be partially initialized, purge it: |
|
367 c.owner = prevOwner; |
|
368 c.members_field.getSymbols(sym -> sym.kind == TYP).forEach(sym -> { |
|
369 ClassSymbol csym = (ClassSymbol) sym; |
|
370 csym.owner = sym.packge(); |
|
371 csym.owner.members().enter(sym); |
|
372 csym.fullname = sym.flatName(); |
|
373 csym.name = Convert.shortName(sym.flatName()); |
|
374 csym.reset(); |
|
375 }); |
|
376 c.fullname = prevName; |
|
377 c.name = Convert.shortName(prevName); |
|
378 c.reset(); |
|
379 throw cf; |
|
380 } finally { |
|
381 currentClassFile = previousClassFile; |
|
382 } |
|
383 } else { |
|
384 throw classFileNotFound(c); |
|
385 } |
|
386 } |
|
387 // where |
|
388 private CompletionFailure classFileNotFound(ClassSymbol c) { |
|
389 JCDiagnostic diag = |
|
390 diagFactory.fragment(Fragments.ClassFileNotFound(c.flatname)); |
|
391 return newCompletionFailure(c, diag); |
|
392 } |
|
393 /** Static factory for CompletionFailure objects. |
|
394 * In practice, only one can be used at a time, so we share one |
|
395 * to reduce the expense of allocating new exception objects. |
|
396 */ |
|
397 private CompletionFailure newCompletionFailure(TypeSymbol c, |
|
398 JCDiagnostic diag) { |
|
399 if (!cacheCompletionFailure) { |
|
400 // log.warning("proc.messager", |
|
401 // Log.getLocalizedString("class.file.not.found", c.flatname)); |
|
402 // c.debug.printStackTrace(); |
|
403 return new CompletionFailure(c, diag); |
|
404 } else { |
|
405 CompletionFailure result = cachedCompletionFailure; |
|
406 result.sym = c; |
|
407 result.diag = diag; |
|
408 return result; |
|
409 } |
|
410 } |
|
411 private final CompletionFailure cachedCompletionFailure = |
|
412 new CompletionFailure(null, (JCDiagnostic) null); |
|
413 { |
|
414 cachedCompletionFailure.setStackTrace(new StackTraceElement[0]); |
|
415 } |
|
416 |
|
417 |
|
418 /** Load a toplevel class with given fully qualified name |
|
419 * The class is entered into `classes' only if load was successful. |
|
420 */ |
|
421 public ClassSymbol loadClass(ModuleSymbol msym, Name flatname) throws CompletionFailure { |
|
422 Assert.checkNonNull(msym); |
|
423 Name packageName = Convert.packagePart(flatname); |
|
424 PackageSymbol ps = syms.lookupPackage(msym, packageName); |
|
425 |
|
426 Assert.checkNonNull(ps.modle, () -> "msym=" + msym + "; flatName=" + flatname); |
|
427 |
|
428 boolean absent = syms.getClass(ps.modle, flatname) == null; |
|
429 ClassSymbol c = syms.enterClass(ps.modle, flatname); |
|
430 |
|
431 if (c.members_field == null) { |
|
432 try { |
|
433 c.complete(); |
|
434 } catch (CompletionFailure ex) { |
|
435 if (absent) syms.removeClass(ps.modle, flatname); |
|
436 throw ex; |
|
437 } |
|
438 } |
|
439 return c; |
|
440 } |
|
441 |
|
442 /************************************************************************ |
|
443 * Loading Packages |
|
444 ***********************************************************************/ |
|
445 |
|
446 /** Include class corresponding to given class file in package, |
|
447 * unless (1) we already have one the same kind (.class or .java), or |
|
448 * (2) we have one of the other kind, and the given class file |
|
449 * is older. |
|
450 */ |
|
451 protected void includeClassFile(PackageSymbol p, JavaFileObject file) { |
|
452 if ((p.flags_field & EXISTS) == 0) |
|
453 for (Symbol q = p; q != null && q.kind == PCK; q = q.owner) |
|
454 q.flags_field |= EXISTS; |
|
455 JavaFileObject.Kind kind = file.getKind(); |
|
456 int seen; |
|
457 if (kind == JavaFileObject.Kind.CLASS || kind == JavaFileObject.Kind.OTHER) |
|
458 seen = CLASS_SEEN; |
|
459 else |
|
460 seen = SOURCE_SEEN; |
|
461 String binaryName = fileManager.inferBinaryName(currentLoc, file); |
|
462 int lastDot = binaryName.lastIndexOf("."); |
|
463 Name classname = names.fromString(binaryName.substring(lastDot + 1)); |
|
464 boolean isPkgInfo = classname == names.package_info; |
|
465 ClassSymbol c = isPkgInfo |
|
466 ? p.package_info |
|
467 : (ClassSymbol) p.members_field.findFirst(classname); |
|
468 if (c == null) { |
|
469 c = syms.enterClass(p.modle, classname, p); |
|
470 if (c.classfile == null) // only update the file if's it's newly created |
|
471 c.classfile = file; |
|
472 if (isPkgInfo) { |
|
473 p.package_info = c; |
|
474 } else { |
|
475 if (c.owner == p) // it might be an inner class |
|
476 p.members_field.enter(c); |
|
477 } |
|
478 } else if (!preferCurrent && c.classfile != null && (c.flags_field & seen) == 0) { |
|
479 // if c.classfile == null, we are currently compiling this class |
|
480 // and no further action is necessary. |
|
481 // if (c.flags_field & seen) != 0, we have already encountered |
|
482 // a file of the same kind; again no further action is necessary. |
|
483 if ((c.flags_field & (CLASS_SEEN | SOURCE_SEEN)) != 0) |
|
484 c.classfile = preferredFileObject(file, c.classfile); |
|
485 } |
|
486 c.flags_field |= seen; |
|
487 } |
|
488 |
|
489 /** Implement policy to choose to derive information from a source |
|
490 * file or a class file when both are present. May be overridden |
|
491 * by subclasses. |
|
492 */ |
|
493 protected JavaFileObject preferredFileObject(JavaFileObject a, |
|
494 JavaFileObject b) { |
|
495 |
|
496 if (preferSource) |
|
497 return (a.getKind() == JavaFileObject.Kind.SOURCE) ? a : b; |
|
498 else { |
|
499 long adate = a.getLastModified(); |
|
500 long bdate = b.getLastModified(); |
|
501 // 6449326: policy for bad lastModifiedTime in ClassReader |
|
502 //assert adate >= 0 && bdate >= 0; |
|
503 return (adate > bdate) ? a : b; |
|
504 } |
|
505 } |
|
506 |
|
507 /** |
|
508 * specifies types of files to be read when filling in a package symbol |
|
509 */ |
|
510 // Note: overridden by JavadocClassFinder |
|
511 protected EnumSet<JavaFileObject.Kind> getPackageFileKinds() { |
|
512 return EnumSet.of(JavaFileObject.Kind.CLASS, JavaFileObject.Kind.SOURCE); |
|
513 } |
|
514 |
|
515 /** |
|
516 * this is used to support javadoc |
|
517 */ |
|
518 protected void extraFileActions(PackageSymbol pack, JavaFileObject fe) { |
|
519 } |
|
520 |
|
521 protected Location currentLoc; // FIXME |
|
522 |
|
523 private boolean verbosePath = true; |
|
524 |
|
525 // Set to true when the currently selected file should be kept |
|
526 private boolean preferCurrent; |
|
527 |
|
528 /** Load directory of package into members scope. |
|
529 */ |
|
530 private void fillIn(PackageSymbol p) throws IOException { |
|
531 if (p.members_field == null) |
|
532 p.members_field = WriteableScope.create(p); |
|
533 |
|
534 ModuleSymbol msym = p.modle; |
|
535 |
|
536 Assert.checkNonNull(msym, p::toString); |
|
537 |
|
538 msym.complete(); |
|
539 |
|
540 if (msym == syms.noModule) { |
|
541 preferCurrent = false; |
|
542 if (userPathsFirst) { |
|
543 scanUserPaths(p, true); |
|
544 preferCurrent = true; |
|
545 scanPlatformPath(p); |
|
546 } else { |
|
547 scanPlatformPath(p); |
|
548 scanUserPaths(p, true); |
|
549 } |
|
550 } else if (msym.classLocation == StandardLocation.CLASS_PATH) { |
|
551 scanUserPaths(p, msym.sourceLocation == StandardLocation.SOURCE_PATH); |
|
552 } else { |
|
553 scanModulePaths(p, msym); |
|
554 } |
|
555 } |
|
556 |
|
557 // TODO: for now, this is a much simplified form of scanUserPaths |
|
558 // and (deliberately) does not default sourcepath to classpath. |
|
559 // But, we need to think about retaining existing behavior for |
|
560 // -classpath and -sourcepath for single module mode. |
|
561 // One plausible solution is to detect if the module's sourceLocation |
|
562 // is the same as the module's classLocation. |
|
563 private void scanModulePaths(PackageSymbol p, ModuleSymbol msym) throws IOException { |
|
564 Set<JavaFileObject.Kind> kinds = getPackageFileKinds(); |
|
565 |
|
566 Set<JavaFileObject.Kind> classKinds = EnumSet.copyOf(kinds); |
|
567 classKinds.remove(JavaFileObject.Kind.SOURCE); |
|
568 boolean wantClassFiles = !classKinds.isEmpty(); |
|
569 |
|
570 Set<JavaFileObject.Kind> sourceKinds = EnumSet.copyOf(kinds); |
|
571 sourceKinds.remove(JavaFileObject.Kind.CLASS); |
|
572 boolean wantSourceFiles = !sourceKinds.isEmpty(); |
|
573 |
|
574 String packageName = p.fullname.toString(); |
|
575 |
|
576 Location classLocn = msym.classLocation; |
|
577 Location sourceLocn = msym.sourceLocation; |
|
578 Location patchLocn = msym.patchLocation; |
|
579 Location patchOutLocn = msym.patchOutputLocation; |
|
580 |
|
581 boolean prevPreferCurrent = preferCurrent; |
|
582 |
|
583 try { |
|
584 preferCurrent = false; |
|
585 if (wantClassFiles && (patchOutLocn != null)) { |
|
586 fillIn(p, patchOutLocn, |
|
587 list(patchOutLocn, |
|
588 p, |
|
589 packageName, |
|
590 classKinds)); |
|
591 } |
|
592 if ((wantClassFiles || wantSourceFiles) && (patchLocn != null)) { |
|
593 Set<JavaFileObject.Kind> combined = EnumSet.noneOf(JavaFileObject.Kind.class); |
|
594 combined.addAll(classKinds); |
|
595 combined.addAll(sourceKinds); |
|
596 fillIn(p, patchLocn, |
|
597 list(patchLocn, |
|
598 p, |
|
599 packageName, |
|
600 combined)); |
|
601 } |
|
602 preferCurrent = true; |
|
603 if (wantClassFiles && (classLocn != null)) { |
|
604 fillIn(p, classLocn, |
|
605 list(classLocn, |
|
606 p, |
|
607 packageName, |
|
608 classKinds)); |
|
609 } |
|
610 if (wantSourceFiles && (sourceLocn != null)) { |
|
611 fillIn(p, sourceLocn, |
|
612 list(sourceLocn, |
|
613 p, |
|
614 packageName, |
|
615 sourceKinds)); |
|
616 } |
|
617 } finally { |
|
618 preferCurrent = prevPreferCurrent; |
|
619 } |
|
620 } |
|
621 |
|
622 /** |
|
623 * Scans class path and source path for files in given package. |
|
624 */ |
|
625 private void scanUserPaths(PackageSymbol p, boolean includeSourcePath) throws IOException { |
|
626 Set<JavaFileObject.Kind> kinds = getPackageFileKinds(); |
|
627 |
|
628 Set<JavaFileObject.Kind> classKinds = EnumSet.copyOf(kinds); |
|
629 classKinds.remove(JavaFileObject.Kind.SOURCE); |
|
630 boolean wantClassFiles = !classKinds.isEmpty(); |
|
631 |
|
632 Set<JavaFileObject.Kind> sourceKinds = EnumSet.copyOf(kinds); |
|
633 sourceKinds.remove(JavaFileObject.Kind.CLASS); |
|
634 boolean wantSourceFiles = !sourceKinds.isEmpty(); |
|
635 |
|
636 boolean haveSourcePath = includeSourcePath && fileManager.hasLocation(SOURCE_PATH); |
|
637 |
|
638 if (verbose && verbosePath) { |
|
639 if (fileManager instanceof StandardJavaFileManager) { |
|
640 StandardJavaFileManager fm = (StandardJavaFileManager)fileManager; |
|
641 if (haveSourcePath && wantSourceFiles) { |
|
642 List<Path> path = List.nil(); |
|
643 for (Path sourcePath : fm.getLocationAsPaths(SOURCE_PATH)) { |
|
644 path = path.prepend(sourcePath); |
|
645 } |
|
646 log.printVerbose("sourcepath", path.reverse().toString()); |
|
647 } else if (wantSourceFiles) { |
|
648 List<Path> path = List.nil(); |
|
649 for (Path classPath : fm.getLocationAsPaths(CLASS_PATH)) { |
|
650 path = path.prepend(classPath); |
|
651 } |
|
652 log.printVerbose("sourcepath", path.reverse().toString()); |
|
653 } |
|
654 if (wantClassFiles) { |
|
655 List<Path> path = List.nil(); |
|
656 for (Path platformPath : fm.getLocationAsPaths(PLATFORM_CLASS_PATH)) { |
|
657 path = path.prepend(platformPath); |
|
658 } |
|
659 for (Path classPath : fm.getLocationAsPaths(CLASS_PATH)) { |
|
660 path = path.prepend(classPath); |
|
661 } |
|
662 log.printVerbose("classpath", path.reverse().toString()); |
|
663 } |
|
664 } |
|
665 } |
|
666 |
|
667 String packageName = p.fullname.toString(); |
|
668 if (wantSourceFiles && !haveSourcePath) { |
|
669 fillIn(p, CLASS_PATH, |
|
670 list(CLASS_PATH, |
|
671 p, |
|
672 packageName, |
|
673 kinds)); |
|
674 } else { |
|
675 if (wantClassFiles) |
|
676 fillIn(p, CLASS_PATH, |
|
677 list(CLASS_PATH, |
|
678 p, |
|
679 packageName, |
|
680 classKinds)); |
|
681 if (wantSourceFiles) |
|
682 fillIn(p, SOURCE_PATH, |
|
683 list(SOURCE_PATH, |
|
684 p, |
|
685 packageName, |
|
686 sourceKinds)); |
|
687 } |
|
688 } |
|
689 |
|
690 /** |
|
691 * Scans platform class path for files in given package. |
|
692 */ |
|
693 private void scanPlatformPath(PackageSymbol p) throws IOException { |
|
694 fillIn(p, PLATFORM_CLASS_PATH, |
|
695 list(PLATFORM_CLASS_PATH, |
|
696 p, |
|
697 p.fullname.toString(), |
|
698 allowSigFiles ? EnumSet.of(JavaFileObject.Kind.CLASS, |
|
699 JavaFileObject.Kind.OTHER) |
|
700 : EnumSet.of(JavaFileObject.Kind.CLASS))); |
|
701 } |
|
702 // where |
|
703 @SuppressWarnings("fallthrough") |
|
704 private void fillIn(PackageSymbol p, |
|
705 Location location, |
|
706 Iterable<JavaFileObject> files) |
|
707 { |
|
708 currentLoc = location; |
|
709 for (JavaFileObject fo : files) { |
|
710 switch (fo.getKind()) { |
|
711 case OTHER: |
|
712 if (!isSigFile(location, fo)) { |
|
713 extraFileActions(p, fo); |
|
714 break; |
|
715 } |
|
716 //intentional fall-through: |
|
717 case CLASS: |
|
718 case SOURCE: { |
|
719 // TODO pass binaryName to includeClassFile |
|
720 String binaryName = fileManager.inferBinaryName(currentLoc, fo); |
|
721 String simpleName = binaryName.substring(binaryName.lastIndexOf(".") + 1); |
|
722 if (SourceVersion.isIdentifier(simpleName) || |
|
723 simpleName.equals("package-info")) |
|
724 includeClassFile(p, fo); |
|
725 break; |
|
726 } |
|
727 default: |
|
728 extraFileActions(p, fo); |
|
729 break; |
|
730 } |
|
731 } |
|
732 } |
|
733 |
|
734 boolean isSigFile(Location location, JavaFileObject fo) { |
|
735 return location == PLATFORM_CLASS_PATH && |
|
736 allowSigFiles && |
|
737 fo.getName().endsWith(".sig"); |
|
738 } |
|
739 |
|
740 Iterable<JavaFileObject> list(Location location, |
|
741 PackageSymbol p, |
|
742 String packageName, |
|
743 Set<Kind> kinds) throws IOException { |
|
744 Iterable<JavaFileObject> listed = fileManager.list(location, |
|
745 packageName, |
|
746 EnumSet.allOf(Kind.class), |
|
747 false); |
|
748 return () -> new Iterator<JavaFileObject>() { |
|
749 private final Iterator<JavaFileObject> original = listed.iterator(); |
|
750 private JavaFileObject next; |
|
751 @Override |
|
752 public boolean hasNext() { |
|
753 if (next == null) { |
|
754 while (original.hasNext()) { |
|
755 JavaFileObject fo = original.next(); |
|
756 |
|
757 if (fo.getKind() != Kind.CLASS && |
|
758 fo.getKind() != Kind.SOURCE && |
|
759 !isSigFile(currentLoc, fo)) { |
|
760 p.flags_field |= Flags.HAS_RESOURCE; |
|
761 } |
|
762 |
|
763 if (kinds.contains(fo.getKind())) { |
|
764 next = fo; |
|
765 break; |
|
766 } |
|
767 } |
|
768 } |
|
769 return next != null; |
|
770 } |
|
771 |
|
772 @Override |
|
773 public JavaFileObject next() { |
|
774 if (!hasNext()) |
|
775 throw new NoSuchElementException(); |
|
776 JavaFileObject result = next; |
|
777 next = null; |
|
778 return result; |
|
779 } |
|
780 |
|
781 }; |
|
782 } |
|
783 |
|
784 /** |
|
785 * Used for bad class definition files, such as bad .class files or |
|
786 * for .java files with unexpected package or class names. |
|
787 */ |
|
788 public static class BadClassFile extends CompletionFailure { |
|
789 private static final long serialVersionUID = 0; |
|
790 |
|
791 public BadClassFile(TypeSymbol sym, JavaFileObject file, JCDiagnostic diag, |
|
792 JCDiagnostic.Factory diagFactory) { |
|
793 super(sym, createBadClassFileDiagnostic(file, diag, diagFactory)); |
|
794 } |
|
795 // where |
|
796 private static JCDiagnostic createBadClassFileDiagnostic( |
|
797 JavaFileObject file, JCDiagnostic diag, JCDiagnostic.Factory diagFactory) { |
|
798 String key = (file.getKind() == JavaFileObject.Kind.SOURCE |
|
799 ? "bad.source.file.header" : "bad.class.file.header"); |
|
800 return diagFactory.fragment(key, file, diag); |
|
801 } |
|
802 } |
|
803 |
|
804 public static class BadEnclosingMethodAttr extends BadClassFile { |
|
805 private static final long serialVersionUID = 0; |
|
806 |
|
807 public BadEnclosingMethodAttr(TypeSymbol sym, JavaFileObject file, JCDiagnostic diag, |
|
808 JCDiagnostic.Factory diagFactory) { |
|
809 super(sym, file, diag, diagFactory); |
|
810 } |
|
811 } |
|
812 } |