src/share/classes/com/sun/tools/javac/file/Paths.java

Thu, 30 Jul 2009 10:29:53 +0100

author
mcimadamore
date
Thu, 30 Jul 2009 10:29:53 +0100
changeset 341
85fecace920b
parent 285
4ce1c1400334
child 450
4011f49b4af8
permissions
-rw-r--r--

6827648: Extremely slow compilation time for visitor pattern code + generics
Summary: Javac unnecessarily recomputates type-substitutions multiple times
Reviewed-by: jjg

duke@1 1 /*
xdono@54 2 * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
duke@1 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1 4 *
duke@1 5 * This code is free software; you can redistribute it and/or modify it
duke@1 6 * under the terms of the GNU General Public License version 2 only, as
duke@1 7 * published by the Free Software Foundation. Sun designates this
duke@1 8 * particular file as subject to the "Classpath" exception as provided
duke@1 9 * by Sun in the LICENSE file that accompanied this code.
duke@1 10 *
duke@1 11 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1 14 * version 2 for more details (a copy is included in the LICENSE file that
duke@1 15 * accompanied this code).
duke@1 16 *
duke@1 17 * You should have received a copy of the GNU General Public License version
duke@1 18 * 2 along with this work; if not, write to the Free Software Foundation,
duke@1 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1 20 *
duke@1 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@1 22 * CA 95054 USA or visit www.sun.com if you need additional information or
duke@1 23 * have any questions.
duke@1 24 */
duke@1 25
jjg@50 26 package com.sun.tools.javac.file;
jjg@50 27
duke@1 28 import java.io.File;
duke@1 29 import java.io.IOException;
duke@1 30 import java.util.HashMap;
duke@1 31 import java.util.HashSet;
duke@1 32 import java.util.Map;
duke@1 33 import java.util.Set;
duke@1 34 import java.util.Collection;
duke@1 35 import java.util.Collections;
duke@1 36 import java.util.LinkedHashSet;
duke@1 37 import java.util.zip.ZipFile;
duke@1 38 import javax.tools.JavaFileManager.Location;
duke@1 39
jjg@50 40 import com.sun.tools.javac.code.Lint;
jjg@50 41 import com.sun.tools.javac.util.Context;
jjg@151 42 import com.sun.tools.javac.util.ListBuffer;
jjg@50 43 import com.sun.tools.javac.util.Log;
jjg@50 44 import com.sun.tools.javac.util.Options;
jjg@50 45
jjg@50 46 import static javax.tools.StandardLocation.*;
duke@1 47 import static com.sun.tools.javac.main.OptionName.*;
duke@1 48
duke@1 49 /** This class converts command line arguments, environment variables
duke@1 50 * and system properties (in File.pathSeparator-separated String form)
duke@1 51 * into a boot class path, user class path, and source path (in
duke@1 52 * Collection<String> form).
duke@1 53 *
duke@1 54 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
duke@1 55 * you write code that depends on this, you do so at your own risk.
duke@1 56 * This code and its internal interfaces are subject to change or
duke@1 57 * deletion without notice.</b>
duke@1 58 */
duke@1 59 public class Paths {
duke@1 60
duke@1 61 /** The context key for the todo list */
duke@1 62 protected static final Context.Key<Paths> pathsKey =
duke@1 63 new Context.Key<Paths>();
duke@1 64
jjg@14 65 /** Get the Paths instance for this context.
jjg@14 66 * @param context the context
jjg@14 67 * @return the Paths instance for this context
jjg@14 68 */
jjg@285 69 static Paths instance(Context context) {
duke@1 70 Paths instance = context.get(pathsKey);
duke@1 71 if (instance == null)
duke@1 72 instance = new Paths(context);
duke@1 73 return instance;
duke@1 74 }
duke@1 75
duke@1 76 /** The log to use for warning output */
duke@1 77 private Log log;
duke@1 78
duke@1 79 /** Collection of command-line options */
duke@1 80 private Options options;
duke@1 81
duke@1 82 /** Handler for -Xlint options */
duke@1 83 private Lint lint;
duke@1 84
jjg@106 85 /** Access to (possibly cached) file info */
jjg@106 86 private FSInfo fsInfo;
duke@1 87
duke@1 88 protected Paths(Context context) {
duke@1 89 context.put(pathsKey, this);
duke@1 90 pathsForLocation = new HashMap<Location,Path>(16);
duke@1 91 setContext(context);
duke@1 92 }
duke@1 93
duke@1 94 void setContext(Context context) {
duke@1 95 log = Log.instance(context);
duke@1 96 options = Options.instance(context);
duke@1 97 lint = Lint.instance(context);
jjg@106 98 fsInfo = FSInfo.instance(context);
duke@1 99 }
duke@1 100
duke@1 101 /** Whether to warn about non-existent path elements */
duke@1 102 private boolean warn;
duke@1 103
duke@1 104 private Map<Location, Path> pathsForLocation;
duke@1 105
duke@1 106 private boolean inited = false; // TODO? caching bad?
duke@1 107
duke@1 108 /**
duke@1 109 * rt.jar as found on the default bootclass path. If the user specified a
duke@1 110 * bootclasspath, null is used.
duke@1 111 */
duke@1 112 private File bootClassPathRtJar = null;
duke@1 113
duke@1 114 Path getPathForLocation(Location location) {
duke@1 115 Path path = pathsForLocation.get(location);
duke@1 116 if (path == null)
duke@1 117 setPathForLocation(location, null);
duke@1 118 return pathsForLocation.get(location);
duke@1 119 }
duke@1 120
duke@1 121 void setPathForLocation(Location location, Iterable<? extends File> path) {
duke@1 122 // TODO? if (inited) throw new IllegalStateException
duke@1 123 // TODO: otherwise reset sourceSearchPath, classSearchPath as needed
duke@1 124 Path p;
duke@1 125 if (path == null) {
duke@1 126 if (location == CLASS_PATH)
duke@1 127 p = computeUserClassPath();
duke@1 128 else if (location == PLATFORM_CLASS_PATH)
duke@1 129 p = computeBootClassPath();
duke@1 130 else if (location == ANNOTATION_PROCESSOR_PATH)
duke@1 131 p = computeAnnotationProcessorPath();
duke@1 132 else if (location == SOURCE_PATH)
duke@1 133 p = computeSourcePath();
duke@1 134 else
duke@1 135 // no defaults for other paths
duke@1 136 p = null;
duke@1 137 } else {
duke@1 138 p = new Path();
duke@1 139 for (File f: path)
duke@1 140 p.addFile(f, warn); // TODO: is use of warn appropriate?
duke@1 141 }
duke@1 142 pathsForLocation.put(location, p);
duke@1 143 }
duke@1 144
duke@1 145 protected void lazy() {
duke@1 146 if (!inited) {
duke@1 147 warn = lint.isEnabled(Lint.LintCategory.PATH);
duke@1 148
duke@1 149 pathsForLocation.put(PLATFORM_CLASS_PATH, computeBootClassPath());
duke@1 150 pathsForLocation.put(CLASS_PATH, computeUserClassPath());
duke@1 151 pathsForLocation.put(SOURCE_PATH, computeSourcePath());
duke@1 152
duke@1 153 inited = true;
duke@1 154 }
duke@1 155 }
duke@1 156
duke@1 157 public Collection<File> bootClassPath() {
duke@1 158 lazy();
duke@1 159 return Collections.unmodifiableCollection(getPathForLocation(PLATFORM_CLASS_PATH));
duke@1 160 }
duke@1 161 public Collection<File> userClassPath() {
duke@1 162 lazy();
duke@1 163 return Collections.unmodifiableCollection(getPathForLocation(CLASS_PATH));
duke@1 164 }
duke@1 165 public Collection<File> sourcePath() {
duke@1 166 lazy();
duke@1 167 Path p = getPathForLocation(SOURCE_PATH);
duke@1 168 return p == null || p.size() == 0
duke@1 169 ? null
duke@1 170 : Collections.unmodifiableCollection(p);
duke@1 171 }
duke@1 172
duke@1 173 boolean isBootClassPathRtJar(File file) {
duke@1 174 return file.equals(bootClassPathRtJar);
duke@1 175 }
duke@1 176
jjg@151 177 /**
jjg@151 178 * Split a path into its elements. Empty path elements will be ignored.
jjg@151 179 * @param path The path to be split
jjg@151 180 * @return The elements of the path
jjg@151 181 */
jjg@151 182 private static Iterable<File> getPathEntries(String path) {
jjg@151 183 return getPathEntries(path, null);
jjg@151 184 }
duke@1 185
jjg@151 186 /**
jjg@151 187 * Split a path into its elements. If emptyPathDefault is not null, all
jjg@151 188 * empty elements in the path, including empty elements at either end of
jjg@151 189 * the path, will be replaced with the value of emptyPathDefault.
jjg@151 190 * @param path The path to be split
jjg@151 191 * @param emptyPathDefault The value to substitute for empty path elements,
jjg@151 192 * or null, to ignore empty path elements
jjg@151 193 * @return The elements of the path
jjg@151 194 */
jjg@151 195 private static Iterable<File> getPathEntries(String path, File emptyPathDefault) {
jjg@151 196 ListBuffer<File> entries = new ListBuffer<File>();
jjg@151 197 int start = 0;
jjg@151 198 while (start <= path.length()) {
jjg@151 199 int sep = path.indexOf(File.pathSeparatorChar, start);
jjg@151 200 if (sep == -1)
jjg@151 201 sep = path.length();
jjg@151 202 if (start < sep)
jjg@151 203 entries.add(new File(path.substring(start, sep)));
jjg@151 204 else if (emptyPathDefault != null)
jjg@151 205 entries.add(emptyPathDefault);
jjg@151 206 start = sep + 1;
duke@1 207 }
jjg@151 208 return entries;
duke@1 209 }
duke@1 210
duke@1 211 private class Path extends LinkedHashSet<File> {
duke@1 212 private static final long serialVersionUID = 0;
duke@1 213
duke@1 214 private boolean expandJarClassPaths = false;
duke@1 215 private Set<File> canonicalValues = new HashSet<File>();
duke@1 216
duke@1 217 public Path expandJarClassPaths(boolean x) {
duke@1 218 expandJarClassPaths = x;
duke@1 219 return this;
duke@1 220 }
duke@1 221
duke@1 222 /** What to use when path element is the empty string */
jjg@151 223 private File emptyPathDefault = null;
duke@1 224
jjg@151 225 public Path emptyPathDefault(File x) {
duke@1 226 emptyPathDefault = x;
duke@1 227 return this;
duke@1 228 }
duke@1 229
duke@1 230 public Path() { super(); }
duke@1 231
duke@1 232 public Path addDirectories(String dirs, boolean warn) {
duke@1 233 if (dirs != null)
jjg@151 234 for (File dir : getPathEntries(dirs))
duke@1 235 addDirectory(dir, warn);
duke@1 236 return this;
duke@1 237 }
duke@1 238
duke@1 239 public Path addDirectories(String dirs) {
duke@1 240 return addDirectories(dirs, warn);
duke@1 241 }
duke@1 242
jjg@151 243 private void addDirectory(File dir, boolean warn) {
jjg@151 244 if (!dir.isDirectory()) {
duke@1 245 if (warn)
duke@1 246 log.warning("dir.path.element.not.found", dir);
duke@1 247 return;
duke@1 248 }
duke@1 249
jjg@151 250 File[] files = dir.listFiles();
duke@1 251 if (files == null)
duke@1 252 return;
duke@1 253
duke@1 254 for (File direntry : files) {
duke@1 255 if (isArchive(direntry))
duke@1 256 addFile(direntry, warn);
duke@1 257 }
duke@1 258 }
duke@1 259
duke@1 260 public Path addFiles(String files, boolean warn) {
duke@1 261 if (files != null)
jjg@151 262 for (File file : getPathEntries(files, emptyPathDefault))
duke@1 263 addFile(file, warn);
duke@1 264 return this;
duke@1 265 }
duke@1 266
duke@1 267 public Path addFiles(String files) {
duke@1 268 return addFiles(files, warn);
duke@1 269 }
duke@1 270
duke@1 271 public void addFile(File file, boolean warn) {
jjg@106 272 File canonFile = fsInfo.getCanonicalFile(file);
jjg@106 273 if (contains(file) || canonicalValues.contains(canonFile)) {
duke@1 274 /* Discard duplicates and avoid infinite recursion */
duke@1 275 return;
duke@1 276 }
duke@1 277
jjg@106 278 if (! fsInfo.exists(file)) {
duke@1 279 /* No such file or directory exists */
duke@1 280 if (warn)
duke@1 281 log.warning("path.element.not.found", file);
jjg@106 282 } else if (fsInfo.isFile(file)) {
duke@1 283 /* File is an ordinary file. */
duke@1 284 if (!isArchive(file)) {
duke@1 285 /* Not a recognized extension; open it to see if
duke@1 286 it looks like a valid zip file. */
duke@1 287 try {
duke@1 288 ZipFile z = new ZipFile(file);
duke@1 289 z.close();
duke@1 290 if (warn)
duke@1 291 log.warning("unexpected.archive.file", file);
duke@1 292 } catch (IOException e) {
duke@1 293 // FIXME: include e.getLocalizedMessage in warning
duke@1 294 if (warn)
duke@1 295 log.warning("invalid.archive.file", file);
duke@1 296 return;
duke@1 297 }
duke@1 298 }
duke@1 299 }
duke@1 300
duke@1 301 /* Now what we have left is either a directory or a file name
duke@1 302 confirming to archive naming convention */
duke@1 303 super.add(file);
jjg@106 304 canonicalValues.add(canonFile);
duke@1 305
jjg@106 306 if (expandJarClassPaths && fsInfo.exists(file) && fsInfo.isFile(file))
duke@1 307 addJarClassPath(file, warn);
duke@1 308 }
duke@1 309
duke@1 310 // Adds referenced classpath elements from a jar's Class-Path
duke@1 311 // Manifest entry. In some future release, we may want to
duke@1 312 // update this code to recognize URLs rather than simple
duke@1 313 // filenames, but if we do, we should redo all path-related code.
duke@1 314 private void addJarClassPath(File jarFile, boolean warn) {
duke@1 315 try {
jjg@106 316 for (File f: fsInfo.getJarClassPath(jarFile)) {
jjg@106 317 addFile(f, warn);
duke@1 318 }
duke@1 319 } catch (IOException e) {
duke@1 320 log.error("error.reading.file", jarFile, e.getLocalizedMessage());
duke@1 321 }
duke@1 322 }
duke@1 323 }
duke@1 324
duke@1 325 private Path computeBootClassPath() {
duke@1 326 bootClassPathRtJar = null;
duke@1 327 String optionValue;
duke@1 328 Path path = new Path();
duke@1 329
duke@1 330 path.addFiles(options.get(XBOOTCLASSPATH_PREPEND));
duke@1 331
duke@1 332 if ((optionValue = options.get(ENDORSEDDIRS)) != null)
duke@1 333 path.addDirectories(optionValue);
duke@1 334 else
duke@1 335 path.addDirectories(System.getProperty("java.endorsed.dirs"), false);
duke@1 336
duke@1 337 if ((optionValue = options.get(BOOTCLASSPATH)) != null) {
duke@1 338 path.addFiles(optionValue);
duke@1 339 } else {
duke@1 340 // Standard system classes for this compiler's release.
duke@1 341 String files = System.getProperty("sun.boot.class.path");
duke@1 342 path.addFiles(files, false);
duke@1 343 File rt_jar = new File("rt.jar");
jjg@151 344 for (File file : getPathEntries(files)) {
jjg@151 345 if (new File(file.getName()).equals(rt_jar))
jjg@151 346 bootClassPathRtJar = file;
duke@1 347 }
duke@1 348 }
duke@1 349
duke@1 350 path.addFiles(options.get(XBOOTCLASSPATH_APPEND));
duke@1 351
duke@1 352 // Strictly speaking, standard extensions are not bootstrap
duke@1 353 // classes, but we treat them identically, so we'll pretend
duke@1 354 // that they are.
duke@1 355 if ((optionValue = options.get(EXTDIRS)) != null)
duke@1 356 path.addDirectories(optionValue);
duke@1 357 else
duke@1 358 path.addDirectories(System.getProperty("java.ext.dirs"), false);
duke@1 359
duke@1 360 return path;
duke@1 361 }
duke@1 362
duke@1 363 private Path computeUserClassPath() {
duke@1 364 String cp = options.get(CLASSPATH);
duke@1 365
duke@1 366 // CLASSPATH environment variable when run from `javac'.
duke@1 367 if (cp == null) cp = System.getProperty("env.class.path");
duke@1 368
duke@1 369 // If invoked via a java VM (not the javac launcher), use the
duke@1 370 // platform class path
duke@1 371 if (cp == null && System.getProperty("application.home") == null)
duke@1 372 cp = System.getProperty("java.class.path");
duke@1 373
duke@1 374 // Default to current working directory.
duke@1 375 if (cp == null) cp = ".";
duke@1 376
duke@1 377 return new Path()
jjg@151 378 .expandJarClassPaths(true) // Only search user jars for Class-Paths
jjg@151 379 .emptyPathDefault(new File(".")) // Empty path elt ==> current directory
duke@1 380 .addFiles(cp);
duke@1 381 }
duke@1 382
duke@1 383 private Path computeSourcePath() {
duke@1 384 String sourcePathArg = options.get(SOURCEPATH);
duke@1 385 if (sourcePathArg == null)
duke@1 386 return null;
duke@1 387
duke@1 388 return new Path().addFiles(sourcePathArg);
duke@1 389 }
duke@1 390
duke@1 391 private Path computeAnnotationProcessorPath() {
duke@1 392 String processorPathArg = options.get(PROCESSORPATH);
duke@1 393 if (processorPathArg == null)
duke@1 394 return null;
duke@1 395
duke@1 396 return new Path().addFiles(processorPathArg);
duke@1 397 }
duke@1 398
duke@1 399 /** The actual effective locations searched for sources */
duke@1 400 private Path sourceSearchPath;
duke@1 401
duke@1 402 public Collection<File> sourceSearchPath() {
duke@1 403 if (sourceSearchPath == null) {
duke@1 404 lazy();
duke@1 405 Path sourcePath = getPathForLocation(SOURCE_PATH);
duke@1 406 Path userClassPath = getPathForLocation(CLASS_PATH);
duke@1 407 sourceSearchPath = sourcePath != null ? sourcePath : userClassPath;
duke@1 408 }
duke@1 409 return Collections.unmodifiableCollection(sourceSearchPath);
duke@1 410 }
duke@1 411
duke@1 412 /** The actual effective locations searched for classes */
duke@1 413 private Path classSearchPath;
duke@1 414
duke@1 415 public Collection<File> classSearchPath() {
duke@1 416 if (classSearchPath == null) {
duke@1 417 lazy();
duke@1 418 Path bootClassPath = getPathForLocation(PLATFORM_CLASS_PATH);
duke@1 419 Path userClassPath = getPathForLocation(CLASS_PATH);
duke@1 420 classSearchPath = new Path();
duke@1 421 classSearchPath.addAll(bootClassPath);
duke@1 422 classSearchPath.addAll(userClassPath);
duke@1 423 }
duke@1 424 return Collections.unmodifiableCollection(classSearchPath);
duke@1 425 }
duke@1 426
duke@1 427 /** The actual effective locations for non-source, non-class files */
duke@1 428 private Path otherSearchPath;
duke@1 429
duke@1 430 Collection<File> otherSearchPath() {
duke@1 431 if (otherSearchPath == null) {
duke@1 432 lazy();
duke@1 433 Path userClassPath = getPathForLocation(CLASS_PATH);
duke@1 434 Path sourcePath = getPathForLocation(SOURCE_PATH);
duke@1 435 if (sourcePath == null)
duke@1 436 otherSearchPath = userClassPath;
duke@1 437 else {
duke@1 438 otherSearchPath = new Path();
duke@1 439 otherSearchPath.addAll(userClassPath);
duke@1 440 otherSearchPath.addAll(sourcePath);
duke@1 441 }
duke@1 442 }
duke@1 443 return Collections.unmodifiableCollection(otherSearchPath);
duke@1 444 }
duke@1 445
duke@1 446 /** Is this the name of an archive file? */
jjg@106 447 private boolean isArchive(File file) {
duke@1 448 String n = file.getName().toLowerCase();
jjg@106 449 return fsInfo.isFile(file)
duke@1 450 && (n.endsWith(".jar") || n.endsWith(".zip"));
duke@1 451 }
duke@1 452 }

mercurial