1 /*
   2  * Copyright (c) 1994, 2004, 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 sun.tools.javac;
  27 
  28 import sun.tools.java.*;
  29 import sun.tools.tree.Node;
  30 import sun.tools.java.Package;
  31 
  32 import java.util.*;
  33 import java.io.*;
  34 
  35 /**
  36  * Main environment of the batch version of the Java compiler,
  37  * this needs more work.
  38  *
  39  * WARNING: The contents of this source file are not part of any
  40  * supported API.  Code that depends on them does so at its own risk:
  41  * they are subject to change or removal without notice.
  42  */
  43 @Deprecated
  44 public
  45 class BatchEnvironment extends Environment implements ErrorConsumer {
  46     /**
  47      * The stream where error message are printed.
  48      */
  49     OutputStream out;
  50 
  51     /**
  52      * The path we use for finding source files.
  53      */
  54     protected ClassPath sourcePath;
  55 
  56     /**
  57      * The path we use for finding class (binary) files.
  58      */
  59     protected ClassPath binaryPath;
  60 
  61     /**
  62      * A hashtable of resource contexts.
  63      */
  64     Hashtable<Identifier, Package> packages = new Hashtable<>(31);
  65 
  66     /**
  67      * The classes, in order of appearance.
  68      */
  69     Vector<ClassDeclaration> classesOrdered = new Vector<>();
  70 
  71     /**
  72      * The classes, keyed by ClassDeclaration.
  73      */
  74     Hashtable<Type, ClassDeclaration> classes = new Hashtable<>(351);
  75 
  76     /**
  77      * flags
  78      */
  79     public int flags;
  80 
  81     /**
  82      * Major and minor versions to use for generated class files.
  83      * Environments that extend BatchEnvironment (such as javadoc's
  84      * Env class) get the default values below.
  85      *
  86      * javac itself may override these versions with values determined
  87      * from the command line "-target" option.
  88      */
  89     public short majorVersion = JAVA_DEFAULT_VERSION;
  90     public short minorVersion = JAVA_DEFAULT_MINOR_VERSION;
  91 
  92 // JCOV
  93     /**
  94      * coverage data file
  95      */
  96     public File covFile;
  97 // end JCOV
  98 
  99     /**
 100      * The number of errors and warnings
 101      */
 102     public int nerrors;
 103     public int nwarnings;
 104     public int ndeprecations;
 105 
 106     /**
 107      * A list of files containing deprecation warnings.
 108      */
 109     Vector<Object> deprecationFiles = new Vector<>();
 110 
 111         /**
 112          * writes out error messages
 113          */
 114 
 115         ErrorConsumer errorConsumer;
 116 
 117     /**
 118      * Old constructors -- these constructors build a BatchEnvironment
 119      * with an old-style class path.
 120      */
 121     public BatchEnvironment(ClassPath path) {
 122         this(System.out, path);
 123     }
 124     public BatchEnvironment(OutputStream out,
 125                             ClassPath path) {
 126         this(out, path, (ErrorConsumer) null);
 127     }
 128     public BatchEnvironment(OutputStream out,
 129                             ClassPath path,
 130                             ErrorConsumer errorConsumer) {
 131         this(out, path, path, errorConsumer);
 132     }
 133 
 134     /**
 135      * New constructors -- these constructors build a BatchEnvironment
 136      * with a source path and a binary path.
 137      */
 138     public BatchEnvironment(ClassPath sourcePath,
 139                             ClassPath binaryPath) {
 140         this(System.out, sourcePath, binaryPath);
 141     }
 142     public BatchEnvironment(OutputStream out,
 143                             ClassPath sourcePath,
 144                             ClassPath binaryPath) {
 145         this(out, sourcePath, binaryPath, (ErrorConsumer) null);
 146     }
 147     public BatchEnvironment(OutputStream out,
 148                             ClassPath sourcePath,
 149                             ClassPath binaryPath,
 150                             ErrorConsumer errorConsumer) {
 151         this.out = out;
 152         this.sourcePath = sourcePath;
 153         this.binaryPath = binaryPath;
 154         this.errorConsumer = (errorConsumer == null) ? this : errorConsumer;
 155     }
 156 
 157     /**
 158      * Factory
 159      */
 160     static BatchEnvironment create(OutputStream out,
 161                                    String srcPathString,
 162                                    String classPathString,
 163                                    String sysClassPathString) {
 164         ClassPath[] classPaths = classPaths(srcPathString, classPathString,
 165                                             sysClassPathString);
 166         return new BatchEnvironment(out, classPaths[0], classPaths[1]);
 167     }
 168 
 169     protected static ClassPath[] classPaths(String srcPathString,
 170                                             String classPathString,
 171                                             String sysClassPathString) {
 172         // Create our source classpath and our binary classpath
 173         ClassPath sourcePath;
 174         ClassPath binaryPath;
 175         StringBuffer binaryPathBuffer = new StringBuffer();
 176 
 177         if (classPathString == null) {
 178             // The env.class.path property is the user's CLASSPATH
 179             // environment variable, and it set by the wrapper (ie,
 180             // javac.exe).
 181             classPathString = System.getProperty("env.class.path");
 182             if (classPathString == null) {
 183                 classPathString = ".";
 184             }
 185         }
 186         if (srcPathString == null) {
 187             srcPathString = classPathString;
 188         }
 189         if (sysClassPathString == null) {
 190             sysClassPathString = System.getProperty("sun.boot.class.path");
 191             if (sysClassPathString == null) { // shouldn't happen; recover gracefully
 192                 sysClassPathString = classPathString;
 193             }
 194         }
 195         appendPath(binaryPathBuffer, sysClassPathString);
 196 
 197         appendPath(binaryPathBuffer, classPathString);
 198 
 199         sourcePath = new ClassPath(srcPathString);
 200         binaryPath = new ClassPath(binaryPathBuffer.toString());
 201 
 202         return new ClassPath[]{sourcePath, binaryPath};
 203     }
 204 
 205     private static void appendPath(StringBuffer buf, String str) {
 206         if (str.length() > 0) {
 207             if (buf.length() > 0) {
 208                 buf.append(File.pathSeparator);
 209             }
 210             buf.append(str);
 211         }
 212     }
 213 
 214     /**
 215      * Return flags
 216      */
 217     public int getFlags() {
 218         return flags;
 219     }
 220 
 221     /**
 222      * Return major version to use for generated class files
 223      */
 224     public short getMajorVersion() {
 225         return majorVersion;
 226     }
 227 
 228     /**
 229      * Return minor version to use for generated class files
 230      */
 231     public short getMinorVersion() {
 232         return minorVersion;
 233     }
 234 
 235 // JCOV
 236     /**
 237      * Return coverage data file
 238      */
 239     public File getcovFile() {
 240         return covFile;
 241     }
 242 // end JCOV
 243 
 244     /**
 245      * Return an enumeration of all the currently defined classes
 246      * in order of appearance to getClassDeclaration().
 247      */
 248     public Enumeration<ClassDeclaration> getClasses() {
 249         return classesOrdered.elements();
 250     }
 251 
 252     /**
 253      * A set of Identifiers for all packages exempt from the "exists"
 254      * check in Imports#resolve().  These are the current packages for
 255      * all classes being compiled as of the first call to isExemptPackage.
 256      */
 257     private Set<Identifier> exemptPackages;
 258 
 259     /**
 260      * Tells whether an Identifier refers to a package which should be
 261      * exempt from the "exists" check in Imports#resolve().
 262      */
 263     public boolean isExemptPackage(Identifier id) {
 264         if (exemptPackages == null) {
 265             // Collect a list of the packages of all classes currently
 266             // being compiled.
 267             setExemptPackages();
 268         }
 269 
 270         return exemptPackages.contains(id);
 271     }
 272 
 273     /**
 274      * Set the set of packages which are exempt from the exists check
 275      * in Imports#resolve().
 276      */
 277     private void setExemptPackages() {
 278         // The JLS gives us the freedom to define "accessibility" of
 279         // a package in whatever manner we wish.  After the evaluation
 280         // of bug 4093217, we have decided to consider a package P
 281         // accessible if either:
 282         //
 283         // 1. The directory corresponding to P exists on the classpath.
 284         // 2. For any class C currently being compiled, C belongs to
 285         //    package P.
 286         // 3. For any class C currently being compiled, C belongs to
 287         //    package Q and Q is a subpackage of P.
 288         //
 289         // In order to implement this, we collect the current packages
 290         // (and prefixes) of all packages we have found so far.  These
 291         // will be exempt from the "exists" check in
 292         // sun.tools.java.Imports#resolve().
 293 
 294         exemptPackages = new HashSet<>(101);
 295 
 296         // Add all of the current packages and their prefixes to our set.
 297         for (Enumeration<ClassDeclaration> e = getClasses(); e.hasMoreElements(); ) {
 298             ClassDeclaration c = e.nextElement();
 299             if (c.getStatus() == CS_PARSED) {
 300                 SourceClass def = (SourceClass) c.getClassDefinition();
 301                 if (def.isLocal())
 302                     continue;
 303 
 304                 Identifier pkg = def.getImports().getCurrentPackage();
 305 
 306                 // Add the name of this package and all of its prefixes
 307                 // to our set.
 308                 while (pkg != idNull && exemptPackages.add(pkg)) {
 309                     pkg = pkg.getQualifier();
 310                 }
 311             }
 312         }
 313 
 314         // Before we go any further, we make sure java.lang is
 315         // accessible and that it is not ambiguous.  These checks
 316         // are performed for "ordinary" packages in
 317         // sun.tools.java.Imports#resolve().  The reason we perform
 318         // them specially for java.lang is that we want to report
 319         // the error once, and outside of any particular file.
 320 
 321         // Check to see if java.lang is accessible.
 322         if (!exemptPackages.contains(idJavaLang)) {
 323             // Add java.lang to the set of exempt packages.
 324             exemptPackages.add(idJavaLang);
 325 
 326             try {
 327                 if (!getPackage(idJavaLang).exists()) {
 328                     // java.lang doesn't exist.
 329                     error(0, "package.not.found.strong", idJavaLang);
 330                     return;
 331                 }
 332             } catch (IOException ee) {
 333                 // We got an IO exception checking to see if the package
 334                 // java.lang exists.
 335                 error(0, "io.exception.package", idJavaLang);
 336             }
 337         }
 338 
 339         // Next we ensure that java.lang is not both a class and
 340         // a package.  (Fix for 4101529)
 341         //
 342         // This change has been backed out because, on WIN32, it
 343         // failed to take character case into account.  It will
 344         // be put back in later.
 345         //
 346         // Identifier resolvedName =
 347         //   resolvePackageQualifiedName(idJavaLang);
 348         // Identifier topClassName = resolvedName.getTopName();
 349         //     //if (Imports.importable(topClassName, env)) {
 350         // if (Imports.importable(topClassName, this)) {
 351         //    // It is a package and a class.  Emit the error.
 352         //    error(0, "package.class.conflict.strong",
 353         //            idJavaLang, topClassName);
 354         //    return;
 355         // }
 356     }
 357 
 358     /**
 359      * Get a class, given the fully qualified class name
 360      */
 361     public ClassDeclaration getClassDeclaration(Identifier nm) {
 362         return getClassDeclaration(Type.tClass(nm));
 363     }
 364 
 365     public ClassDeclaration getClassDeclaration(Type t) {
 366         ClassDeclaration c = classes.get(t);
 367         if (c == null) {
 368             classes.put(t, c = new ClassDeclaration(t.getClassName()));
 369             classesOrdered.addElement(c);
 370         }
 371         return c;
 372     }
 373 
 374     /**
 375      * Check if a class exists
 376      * Applies only to package members (non-nested classes).
 377      */
 378     public boolean classExists(Identifier nm) {
 379         if (nm.isInner()) {
 380             nm = nm.getTopName();       // just in case
 381         }
 382         Type t = Type.tClass(nm);
 383         try {
 384             ClassDeclaration c = classes.get(t);
 385             return (c != null) ? c.getName().equals(nm) :
 386                 getPackage(nm.getQualifier()).classExists(nm.getName());
 387         } catch (IOException e) {
 388             return true;
 389         }
 390     }
 391 
 392     /**
 393      * Generate a new name similar to the given one.
 394      * Do it in such a way that repeated compilations of
 395      * the same source generate the same series of names.
 396      */
 397 
 398     // This code does not perform as stated above.
 399     // Correction below is part of fix for bug id 4056065.
 400     //
 401     // NOTE: The method 'generateName' has now been folded into its
 402     // single caller, 'makeClassDefinition', which appears later in
 403     // this file.
 404 
 405     /*--------------------------*
 406     public Identifier generateName(ClassDefinition outerClass, Identifier nm) {
 407         Identifier outerNm = outerClass.getName();
 408         Identifier flat = outerNm.getFlatName();
 409         Identifier stem = Identifier.lookup(outerNm.getQualifier(),
 410                                             flat.getHead());
 411         for (int i = 1; ; i++) {
 412             String name = i + (nm.equals(idNull) ? "" : SIG_INNERCLASS + nm);
 413             Identifier nm1 = Identifier.lookupInner(stem,
 414                                                     Identifier.lookup(name));
 415             if (classes.get(Type.tClass(nm1)) == null)
 416                 return nm1;
 417         }
 418     }
 419     *--------------------------*/
 420 
 421     /**
 422      * Get the package path for a package
 423      */
 424     public Package getPackage(Identifier pkg) throws IOException {
 425         Package p = packages.get(pkg);
 426         if (p == null) {
 427             packages.put(pkg, p = new Package(sourcePath, binaryPath, pkg));
 428         }
 429         return p;
 430     }
 431 
 432     /**
 433      * Parse a source file
 434      */
 435     public void parseFile(ClassFile file) throws FileNotFoundException {
 436         long tm = System.currentTimeMillis();
 437         InputStream input;
 438         BatchParser p;
 439 
 440         if (tracing) dtEnter("parseFile: PARSING SOURCE " + file);
 441 
 442         Environment env = new Environment(this, file);
 443 
 444         try {
 445             input = file.getInputStream();
 446             env.setCharacterEncoding(getCharacterEncoding());
 447             //      p = new BatchParser(e, new BufferedInputStream(input));
 448             p = new BatchParser(env, input);
 449         } catch(IOException ex) {
 450             if (tracing) dtEvent("parseFile: IO EXCEPTION " + file);
 451             throw new FileNotFoundException();
 452         }
 453 
 454         try {
 455             p.parseFile();
 456         } catch(Exception e) {
 457             throw new CompilerError(e);
 458         }
 459 
 460         try {
 461             input.close();
 462         } catch (IOException ex) {
 463             // We're turn with the input, so ignore this.
 464         }
 465 
 466         if (verbose()) {
 467             tm = System.currentTimeMillis() - tm;
 468             output(Main.getText("benv.parsed_in", file.getPath(),
 469                                 Long.toString(tm)));
 470         }
 471 
 472         if (p.classes.size() == 0) {
 473             // The JLS allows a file to contain no compilation units --
 474             // that is, it allows a file to contain no classes or interfaces.
 475             // In this case, we are still responsible for checking that the
 476             // imports resolve properly.  The way the compiler is organized,
 477             // this is the last point at which we still have enough information
 478             // to do so. (Fix for 4041851).
 479             p.imports.resolve(env);
 480         } else {
 481             // In an attempt to see that classes which come from the
 482             // same source file are all recompiled when any one of them
 483             // would be recompiled (when using the -depend option) we
 484             // introduce artificial dependencies between these classes.
 485             // We do this by calling the addDependency() method, which
 486             // adds a (potentially unused) class reference to the constant
 487             // pool of the class.
 488             //
 489             // Previously, we added a dependency from every class in the
 490             // file, to every class in the file.  This introduced, in
 491             // total, a quadratic number of potentially bogus constant
 492             // pool entries.  This was bad.  Now we add our artificial
 493             // dependencies in such a way that the classes are connected
 494             // in a circle.  While single links is probably sufficient, the
 495             // code below adds double links just to be diligent.
 496             // (Fix for 4108286).
 497             //
 498             // Note that we don't chain in inner classes.  The links
 499             // between them and their outerclass should be sufficient
 500             // here.
 501             // (Fix for 4107960).
 502             //
 503             // The dependency code was previously in BatchParser.java.
 504             Enumeration<SourceClass> e = p.classes.elements();
 505 
 506             // first will not be an inner class.
 507             ClassDefinition first = e.nextElement();
 508             if (first.isInnerClass()) {
 509                 throw new CompilerError("BatchEnvironment, first is inner");
 510             }
 511 
 512             ClassDefinition current = first;
 513             ClassDefinition next;
 514             while (e.hasMoreElements()) {
 515                 next = e.nextElement();
 516                 // Don't chain in inner classes.
 517                 if (next.isInnerClass()) {
 518                     continue;
 519                 }
 520                 current.addDependency(next.getClassDeclaration());
 521                 next.addDependency(current.getClassDeclaration());
 522                 current = next;
 523             }
 524             // Make a circle.  Don't bother to add a dependency if there
 525             // is only one class in the file.
 526             if (current != first) {
 527                 current.addDependency(first.getClassDeclaration());
 528                 first.addDependency(current.getClassDeclaration());
 529             }
 530         }
 531 
 532         if (tracing) dtExit("parseFile: SOURCE PARSED " + file);
 533     }
 534 
 535     /**
 536      * Load a binary file
 537      */
 538     BinaryClass loadFile(ClassFile file) throws IOException {
 539         long tm = System.currentTimeMillis();
 540         InputStream input = file.getInputStream();
 541         BinaryClass c = null;
 542 
 543         if (tracing) dtEnter("loadFile: LOADING CLASSFILE " + file);
 544 
 545         try {
 546             DataInputStream is =
 547                 new DataInputStream(new BufferedInputStream(input));
 548             c = BinaryClass.load(new Environment(this, file), is,
 549                                  loadFileFlags());
 550         } catch (ClassFormatError e) {
 551             error(0, "class.format", file.getPath(), e.getMessage());
 552             if (tracing) dtExit("loadFile: CLASS FORMAT ERROR " + file);
 553             return null;
 554         } catch (java.io.EOFException e) {
 555             // If we get an EOF while processing a class file, then
 556             // it has been truncated.  We let other I/O errors pass
 557             // through.  Fix for 4088443.
 558             error(0, "truncated.class", file.getPath());
 559             return null;
 560         }
 561 
 562         input.close();
 563         if (verbose()) {
 564             tm = System.currentTimeMillis() - tm;
 565             output(Main.getText("benv.loaded_in", file.getPath(),
 566                                 Long.toString(tm)));
 567         }
 568 
 569         if (tracing) dtExit("loadFile: CLASSFILE LOADED " + file);
 570 
 571         return c;
 572     }
 573 
 574     /**
 575      * Default flags for loadFile.  Subclasses may override this.
 576      */
 577     int loadFileFlags() {
 578         return 0;
 579     }
 580 
 581     /**
 582      * Load a binary class
 583      */
 584     boolean needsCompilation(Hashtable<ClassDeclaration, ClassDeclaration> check, ClassDeclaration c) {
 585         switch (c.getStatus()) {
 586 
 587           case CS_UNDEFINED:
 588             if (tracing) dtEnter("needsCompilation: UNDEFINED " + c.getName());
 589             loadDefinition(c);
 590             return needsCompilation(check, c);
 591 
 592           case CS_UNDECIDED:
 593             if (tracing) dtEnter("needsCompilation: UNDECIDED " + c.getName());
 594             if (check.get(c) == null) {
 595                 check.put(c, c);
 596 
 597                 BinaryClass bin = (BinaryClass)c.getClassDefinition();
 598                 for (Enumeration<ClassDeclaration> e = bin.getDependencies() ; e.hasMoreElements() ;) {
 599                     ClassDeclaration dep = e.nextElement();
 600                     if (needsCompilation(check, dep)) {
 601                         // It must be source, dependencies need compilation
 602                         c.setDefinition(bin, CS_SOURCE);
 603                         if (tracing) dtExit("needsCompilation: YES (source) " + c.getName());
 604                         return true;
 605                     }
 606                 }
 607             }
 608             if (tracing) dtExit("needsCompilation: NO (undecided) " + c.getName());
 609             return false;
 610 
 611           case CS_BINARY:
 612             if (tracing) {
 613                 dtEnter("needsCompilation: BINARY " + c.getName());
 614                 dtExit("needsCompilation: NO (binary) " + c.getName());
 615             }
 616             return false;
 617 
 618         }
 619 
 620         if (tracing) dtExit("needsCompilation: YES " + c.getName());
 621         return true;
 622     }
 623 
 624     /**
 625      * Load the definition of a class
 626      * or at least determine how to load it.
 627      * The caller must repeat calls to this method
 628      * until it the state converges to CS_BINARY, CS_PARSED, or the like..
 629      * @see ClassDeclaration#getClassDefinition
 630      */
 631     public void loadDefinition(ClassDeclaration c) {
 632         if (tracing) dtEnter("loadDefinition: ENTER " +
 633                              c.getName() + ", status " + c.getStatus());
 634         switch (c.getStatus()) {
 635           case CS_UNDEFINED: {
 636             if (tracing)
 637                 dtEvent("loadDefinition: STATUS IS UNDEFINED");
 638             Identifier nm = c.getName();
 639             Package pkg;
 640             try {
 641                 pkg = getPackage(nm.getQualifier());
 642             } catch (IOException e) {
 643                 // If we can't get at the package, then we'll just
 644                 // have to set the class to be not found.
 645                 c.setDefinition(null, CS_NOTFOUND);
 646 
 647                 error(0, "io.exception", c);
 648                 if (tracing)
 649                     dtExit("loadDefinition: IO EXCEPTION (package)");
 650                 return;
 651             }
 652             ClassFile binfile = pkg.getBinaryFile(nm.getName());
 653             if (binfile == null) {
 654                 // must be source, there is no binary
 655                 c.setDefinition(null, CS_SOURCE);
 656                 if (tracing)
 657                     dtExit("loadDefinition: MUST BE SOURCE (no binary) " +
 658                            c.getName());
 659                 return;
 660             }
 661 
 662             ClassFile srcfile = pkg.getSourceFile(nm.getName());
 663             if (srcfile == null) {
 664                 if (tracing)
 665                     dtEvent("loadDefinition: NO SOURCE " + c.getName());
 666                 BinaryClass bc = null;
 667                 try {
 668                     bc = loadFile(binfile);
 669                 } catch (IOException e) {
 670                     // If we can't access the binary, set the class to
 671                     // be not found.  (bug id 4030497)
 672                     c.setDefinition(null, CS_NOTFOUND);
 673 
 674                     error(0, "io.exception", binfile);
 675                     if (tracing)
 676                         dtExit("loadDefinition: IO EXCEPTION (binary)");
 677                     return;
 678                 }
 679                 if ((bc != null) && !bc.getName().equals(nm)) {
 680                     error(0, "wrong.class", binfile.getPath(), c, bc);
 681                     bc = null;
 682                     if (tracing)
 683                         dtEvent("loadDefinition: WRONG CLASS (binary)");
 684                 }
 685                 if (bc == null) {
 686                     // no source nor binary found
 687                     c.setDefinition(null, CS_NOTFOUND);
 688                     if (tracing)
 689                         dtExit("loadDefinition: NOT FOUND (source or binary)");
 690                     return;
 691                 }
 692 
 693                 // Couldn't find the source, try the one mentioned in the binary
 694                 if (bc.getSource() != null) {
 695                     srcfile = ClassFile.newClassFile(new File((String)bc.getSource()));
 696                     // Look for the source file
 697                     srcfile = pkg.getSourceFile(srcfile.getName());
 698                     if ((srcfile != null) && srcfile.exists()) {
 699                         if (tracing)
 700                             dtEvent("loadDefinition: FILENAME IN BINARY " +
 701                                     srcfile);
 702                         if (srcfile.lastModified() > binfile.lastModified()) {
 703                             // must be source, it is newer than the binary
 704                             c.setDefinition(bc, CS_SOURCE);
 705                             if (tracing)
 706                                 dtEvent("loadDefinition: SOURCE IS NEWER " +
 707                                         srcfile);
 708                             bc.loadNested(this);
 709                             if (tracing)
 710                                 dtExit("loadDefinition: MUST BE SOURCE " +
 711                                        c.getName());
 712                             return;
 713                         }
 714                         if (dependencies()) {
 715                             c.setDefinition(bc, CS_UNDECIDED);
 716                             if (tracing)
 717                                 dtEvent("loadDefinition: UNDECIDED " +
 718                                         c.getName());
 719                         } else {
 720                             c.setDefinition(bc, CS_BINARY);
 721                             if (tracing)
 722                                 dtEvent("loadDefinition: MUST BE BINARY " +
 723                                         c.getName());
 724                         }
 725                         bc.loadNested(this);
 726                         if (tracing)
 727                             dtExit("loadDefinition: EXIT " +
 728                                    c.getName() + ", status " + c.getStatus());
 729                         return;
 730                     }
 731                 }
 732 
 733                 // It must be binary, there is no source
 734                 c.setDefinition(bc, CS_BINARY);
 735                 if (tracing)
 736                     dtEvent("loadDefinition: MUST BE BINARY (no source) " +
 737                                      c.getName());
 738                 bc.loadNested(this);
 739                 if (tracing)
 740                     dtExit("loadDefinition: EXIT " +
 741                            c.getName() + ", status " + c.getStatus());
 742                 return;
 743             }
 744             BinaryClass bc = null;
 745             try {
 746                 if (srcfile.lastModified() > binfile.lastModified()) {
 747                     // must be source, it is newer than the binary
 748                     c.setDefinition(null, CS_SOURCE);
 749                     if (tracing)
 750                         dtEvent("loadDefinition: MUST BE SOURCE (younger than binary) " +
 751                                 c.getName());
 752                     return;
 753                 }
 754                 bc = loadFile(binfile);
 755             } catch (IOException e) {
 756                 error(0, "io.exception", binfile);
 757                 if (tracing)
 758                     dtEvent("loadDefinition: IO EXCEPTION (binary)");
 759             }
 760             if ((bc != null) && !bc.getName().equals(nm)) {
 761                 error(0, "wrong.class", binfile.getPath(), c, bc);
 762                 bc = null;
 763                 if (tracing)
 764                     dtEvent("loadDefinition: WRONG CLASS (binary)");
 765             }
 766             if (bc != null) {
 767                 Identifier name = bc.getName();
 768                 if (name.equals(c.getName())) {
 769                     if (dependencies()) {
 770                         c.setDefinition(bc, CS_UNDECIDED);
 771                         if (tracing)
 772                             dtEvent("loadDefinition: UNDECIDED " + name);
 773                     } else {
 774                         c.setDefinition(bc, CS_BINARY);
 775                         if (tracing)
 776                             dtEvent("loadDefinition: MUST BE BINARY " + name);
 777                     }
 778                 } else {
 779                     c.setDefinition(null, CS_NOTFOUND);
 780                     if (tracing)
 781                         dtEvent("loadDefinition: NOT FOUND (source or binary)");
 782                     if (dependencies()) {
 783                         getClassDeclaration(name).setDefinition(bc, CS_UNDECIDED);
 784                         if (tracing)
 785                             dtEvent("loadDefinition: UNDECIDED " + name);
 786                     } else {
 787                         getClassDeclaration(name).setDefinition(bc, CS_BINARY);
 788                         if (tracing)
 789                             dtEvent("loadDefinition: MUST BE BINARY " + name);
 790                     }
 791                 }
 792             } else {
 793                 c.setDefinition(null, CS_NOTFOUND);
 794                 if (tracing)
 795                     dtEvent("loadDefinition: NOT FOUND (source or binary)");
 796             }
 797             if (bc != null && bc == c.getClassDefinition())
 798                 bc.loadNested(this);
 799             if (tracing) dtExit("loadDefinition: EXIT " +
 800                                 c.getName() + ", status " + c.getStatus());
 801             return;
 802           }
 803 
 804           case CS_UNDECIDED: {
 805             if (tracing) dtEvent("loadDefinition: STATUS IS UNDECIDED");
 806             Hashtable<ClassDeclaration, ClassDeclaration> tab = new Hashtable<>();
 807             if (!needsCompilation(tab, c)) {
 808                 // All undecided classes that this class depends on must be binary
 809                 for (Enumeration<ClassDeclaration> e = tab.keys() ; e.hasMoreElements() ; ) {
 810                     ClassDeclaration dep = e.nextElement();
 811                     if (dep.getStatus() == CS_UNDECIDED) {
 812                         // must be binary, dependencies need compilation
 813                         dep.setDefinition(dep.getClassDefinition(), CS_BINARY);
 814                         if (tracing)
 815                             dtEvent("loadDefinition: MUST BE BINARY " + dep);
 816                     }
 817                 }
 818             }
 819             if (tracing) dtExit("loadDefinition: EXIT " +
 820                                 c.getName() + ", status " + c.getStatus());
 821             return;
 822           }
 823 
 824           case CS_SOURCE: {
 825             if (tracing) dtEvent("loadDefinition: STATUS IS SOURCE");
 826             ClassFile srcfile = null;
 827             Package pkg = null;
 828             if (c.getClassDefinition() != null) {
 829                 // Use the source file name from the binary class file
 830                 try {
 831                     pkg = getPackage(c.getName().getQualifier());
 832                     srcfile = pkg.getSourceFile((String)c.getClassDefinition().getSource());
 833                 } catch (IOException e) {
 834                     error(0, "io.exception", c);
 835                     if (tracing)
 836                         dtEvent("loadDefinition: IO EXCEPTION (package)");
 837                 }
 838                 if (srcfile == null) {
 839                     String fn = (String)c.getClassDefinition().getSource();
 840                     srcfile = ClassFile.newClassFile(new File(fn));
 841                 }
 842             } else {
 843                 // Get a source file name from the package
 844                 Identifier nm = c.getName();
 845                 try {
 846                     pkg = getPackage(nm.getQualifier());
 847                     srcfile = pkg.getSourceFile(nm.getName());
 848                 } catch (IOException e)  {
 849                     error(0, "io.exception", c);
 850                     if (tracing)
 851                         dtEvent("loadDefinition: IO EXCEPTION (package)");
 852                 }
 853                 if (srcfile == null) {
 854                     // not found, there is no source
 855                     c.setDefinition(null, CS_NOTFOUND);
 856                     if (tracing)
 857                         dtExit("loadDefinition: SOURCE NOT FOUND " +
 858                                c.getName() + ", status " + c.getStatus());
 859                     return;
 860                 }
 861             }
 862             try {
 863                 parseFile(srcfile);
 864             } catch (FileNotFoundException e) {
 865                 error(0, "io.exception", srcfile);
 866                 if (tracing) dtEvent("loadDefinition: IO EXCEPTION (source)");
 867             }
 868             if ((c.getClassDefinition() == null) || (c.getStatus() == CS_SOURCE)) {
 869                 // not found after parsing the file
 870                 error(0, "wrong.source", srcfile.getPath(), c, pkg);
 871                 c.setDefinition(null, CS_NOTFOUND);
 872                 if (tracing)
 873                     dtEvent("loadDefinition: WRONG CLASS (source) " +
 874                             c.getName());
 875             }
 876             if (tracing) dtExit("loadDefinition: EXIT " +
 877                                 c.getName() + ", status " + c.getStatus());
 878             return;
 879           }
 880         }
 881         if (tracing) dtExit("loadDefinition: EXIT " +
 882                             c.getName() + ", status " + c.getStatus());
 883     }
 884 
 885     /**
 886      * Create a new class.
 887      */
 888     public ClassDefinition makeClassDefinition(Environment toplevelEnv,
 889                                                long where,
 890                                                IdentifierToken name,
 891                                                String doc, int modifiers,
 892                                                IdentifierToken superClass,
 893                                                IdentifierToken interfaces[],
 894                                                ClassDefinition outerClass) {
 895 
 896         Identifier nm = name.getName();
 897         long nmpos = name.getWhere();
 898 
 899         Identifier pkgNm;
 900         String mangledName = null;
 901         ClassDefinition localContextClass = null;
 902 
 903         // Provide name for a local class.  This used to be set after
 904         // the class was created, but it is needed for checking within
 905         // the class constructor.
 906         // NOTE: It seems that we could always provide the simple name,
 907         // and thereby avoid the test in 'ClassDefinition.getLocalName()'
 908         // for the definedness of the local name.  There, if the local
 909         // name is not set, a simple name is extracted from the result of
 910         // 'getName()'.  That name can potentially change, however, as
 911         // it is ultimately derived from 'ClassType.className', which is
 912         // set by 'Type.changeClassName'.  Better leave this alone...
 913         Identifier localName = null;
 914 
 915         if (nm.isQualified() || nm.isInner()) {
 916             pkgNm = nm;
 917         } else if ((modifiers & (M_LOCAL | M_ANONYMOUS)) != 0) {
 918             // Inaccessible class.  Create a name of the form
 919             // 'PackageMember.N$localName' or 'PackageMember.N'.
 920             // Note that the '.' will be converted later to a '$'.
 921             //   pkgNm = generateName(outerClass, nm);
 922             localContextClass = outerClass.getTopClass();
 923             // Always use the smallest number in generating the name that
 924             // renders the complete name unique within the top-level class.
 925             // This is required to make the names more predictable, as part
 926             // of a serialization-related workaround, and satisfies an obscure
 927             // requirement that the name of a local class be of the form
 928             // 'PackageMember$1$localName' when this name is unique.
 929             for (int i = 1 ; ; i++) {
 930                 mangledName = i + (nm.equals(idNull) ? "" : SIG_INNERCLASS + nm);
 931                 if (localContextClass.getLocalClass(mangledName) == null) {
 932                     break;
 933                 }
 934             }
 935             Identifier outerNm = localContextClass.getName();
 936             pkgNm = Identifier.lookupInner(outerNm, Identifier.lookup(mangledName));
 937             //System.out.println("LOCAL CLASS: " + pkgNm + " IN " + localContextClass);
 938             if ((modifiers & M_ANONYMOUS) != 0) {
 939                 localName = idNull;
 940             } else {
 941                 // Local class has a locally-scoped name which is independent of pkgNm.
 942                 localName = nm;
 943             }
 944         } else if (outerClass != null) {
 945             // Accessible inner class.  Qualify name with surrounding class name.
 946             pkgNm = Identifier.lookupInner(outerClass.getName(), nm);
 947         } else {
 948             pkgNm = nm;
 949         }
 950 
 951         // Find the class
 952         ClassDeclaration c = toplevelEnv.getClassDeclaration(pkgNm);
 953 
 954         // Make sure this is the first definition
 955         if (c.isDefined()) {
 956             toplevelEnv.error(nmpos, "class.multidef",
 957                               c.getName(), c.getClassDefinition().getSource());
 958             // Don't mess with the existing class declarations with same name
 959             c = new ClassDeclaration (pkgNm);
 960         }
 961 
 962         if (superClass == null && !pkgNm.equals(idJavaLangObject)) {
 963             superClass = new IdentifierToken(idJavaLangObject);
 964         }
 965 
 966         ClassDefinition sourceClass =
 967             new SourceClass(toplevelEnv, where, c, doc,
 968                             modifiers, superClass, interfaces,
 969                             (SourceClass) outerClass, localName);
 970 
 971         if (outerClass != null) {
 972             // It is a member of its enclosing class.
 973             outerClass.addMember(toplevelEnv, new SourceMember(sourceClass));
 974             // Record local (or anonymous) class in the class whose name will
 975             // serve as the prefix of the local class name.  This is necessary
 976             // so that the class may be retrieved from its name, which does not
 977             // fully represent the class nesting structure.
 978             // See 'ClassDefinition.getClassDefinition'.
 979             // This is part of a fix for bugid 4054523 and 4030421.
 980             if ((modifiers & (M_LOCAL | M_ANONYMOUS)) != 0) {
 981                 localContextClass.addLocalClass(sourceClass, mangledName);
 982             }
 983         }
 984 
 985         // The local name of an anonymous or local class used to be set here
 986         // with a call to 'setLocalName'.  This has been moved to the constructor
 987         // for 'SourceClass', which now takes a 'localName' argument.
 988 
 989         return sourceClass;
 990     }
 991 
 992     /*
 993      * makeMemberDefinition method is left with rawtypes and with lint messages suppressed.
 994      * The addition of Generics to com.sun.tools.* has uncovered an inconsistency
 995      * in usage though tools still work correctly as long as this function is allowed to
 996      * function as is.
 997      */
 998 
 999     /**
1000      * Create a new field.
1001      */
1002     @SuppressWarnings({"rawtypes","unchecked"})
1003     public MemberDefinition makeMemberDefinition(Environment origEnv, long where,
1004                                                ClassDefinition clazz,
1005                                                String doc, int modifiers,
1006                                                Type type, Identifier name,
1007                                                IdentifierToken argNames[],
1008                                                IdentifierToken expIds[],
1009                                                Object value) {
1010         if (tracing) dtEvent("makeMemberDefinition: " + name + " IN " + clazz);
1011         Vector v = null;
1012         if (argNames != null) {
1013             v = new Vector(argNames.length);
1014             for (int i = 0 ; i < argNames.length ; i++) {
1015                 v.addElement(argNames[i]);
1016             }
1017         }
1018         SourceMember f = new SourceMember(where, clazz, doc, modifiers,
1019                                         type, name, v, expIds, (Node)value);
1020         clazz.addMember(origEnv, f);
1021         return f;
1022     }
1023 
1024     /**
1025      * Release resources in classpath.
1026      */
1027     public void shutdown() {
1028         try {
1029             if (sourcePath != null) {
1030                 sourcePath.close();
1031             }
1032             if (binaryPath != null && binaryPath != sourcePath) {
1033                 binaryPath.close();
1034             }
1035         } catch (IOException ee) {
1036             output(Main.getText("benv.failed_to_close_class_path",
1037                                 ee.toString()));
1038         }
1039         sourcePath = null;
1040         binaryPath = null;
1041 
1042         super.shutdown();
1043     }
1044 
1045     /**
1046      * Error String
1047      */
1048     public
1049     String errorString(String err, Object arg1, Object arg2, Object arg3) {
1050         String key = null;
1051 
1052         if(err.startsWith("warn."))
1053             key = "javac.err." + err.substring(5);
1054         else
1055             key = "javac.err." + err;
1056 
1057         return Main.getText(key,
1058                             arg1 != null ? arg1.toString() : null,
1059                             arg2 != null ? arg2.toString() : null,
1060                             arg3 != null ? arg3.toString() : null);
1061     }
1062 
1063     /**
1064      * The filename where the last errors have occurred
1065      */
1066     String errorFileName;
1067 
1068     /**
1069      * List of outstanding error messages
1070      */
1071     ErrorMessage errors;
1072 
1073     /**
1074      * Insert an error message in the list of outstanding error messages.
1075      * The list is sorted on input position and contains no duplicates.
1076      * The return value indicates whether or not the message was
1077      * actually inserted.
1078      *
1079      * The method flushErrors() used to check for duplicate error messages.
1080      * It would only detect duplicates if they were contiguous.  Removing
1081      * non-contiguous duplicate error messages is slightly less complicated
1082      * at insertion time, so the functionality was moved here.  This also
1083      * saves a miniscule number of allocations.
1084      */
1085     protected
1086     boolean insertError(long where, String message) {
1087         //output("ERR = " + message);
1088 
1089         if (errors == null
1090             ||  errors.where > where) {
1091             // If the list is empty, or the error comes before any other
1092             // errors, insert it at the beginning of the list.
1093             ErrorMessage newMsg = new ErrorMessage(where, message);
1094             newMsg.next = errors;
1095             errors = newMsg;
1096 
1097         } else if (errors.where == where
1098                    && errors.message.equals(message)) {
1099             // The new message is an exact duplicate of the first message
1100             // in the list.  Don't insert it.
1101             return false;
1102 
1103         } else {
1104             // Okay, we know that the error doesn't come first.  Walk
1105             // the list until we find the right position for insertion.
1106             ErrorMessage current = errors;
1107             ErrorMessage next;
1108 
1109             while ((next = current.next) != null
1110                    && next.where < where) {
1111                 current = next;
1112             }
1113 
1114             // Now walk over any errors with the same location, looking
1115             // for duplicates.  If we find a duplicate, don't insert the
1116             // error.
1117             while ((next = current.next) != null
1118                    && next.where == where) {
1119                 if (next.message.equals(message)) {
1120                     // We have found an exact duplicate.  Don't bother to
1121                     // insert the error.
1122                     return false;
1123                 }
1124                 current = next;
1125             }
1126 
1127             // Now insert after current.
1128             ErrorMessage newMsg = new ErrorMessage(where, message);
1129             newMsg.next = current.next;
1130             current.next = newMsg;
1131         }
1132 
1133         // Indicate that the insertion occurred.
1134         return true;
1135     }
1136 
1137     private int errorsPushed;
1138 
1139     /**
1140      * Maximum number of errors to print.
1141      */
1142     public int errorLimit = 100;
1143 
1144     private boolean hitErrorLimit;
1145 
1146     /**
1147      * Flush outstanding errors
1148      */
1149 
1150         public void pushError(String errorFileName, int line, String message,
1151                                     String referenceText, String referenceTextPointer) {
1152                 int limit = errorLimit + nwarnings;
1153                 if (++errorsPushed >= limit && errorLimit >= 0) {
1154                     if (!hitErrorLimit) {
1155                         hitErrorLimit = true;
1156                         output(errorString("too.many.errors",
1157                                            errorLimit,null,null));
1158                     }
1159                     return;
1160                 }
1161                 if (errorFileName.endsWith(".java")) {
1162                     output(errorFileName + ":" + line + ": " + message);
1163                     output(referenceText);
1164                     output(referenceTextPointer);
1165                 } else {
1166                     // It wasn't really a source file (probably an error or
1167                     // warning because of a malformed or badly versioned
1168                     // class file.
1169                     output(errorFileName + ": " + message);
1170                 }
1171         }
1172 
1173     public void flushErrors() {
1174         if (errors == null) {
1175             return;
1176         }
1177 
1178         boolean inputAvail = false;
1179         // Read the file
1180         char data[] = null;
1181         int dataLength = 0;
1182         // A malformed file encoding could cause a CharConversionException.
1183         // If something bad happens while trying to find the source file,
1184         // don't bother trying to show lines.
1185         try {
1186             FileInputStream in = new FileInputStream(errorFileName);
1187             data = new char[in.available()];
1188             InputStreamReader reader =
1189                 (getCharacterEncoding() != null ?
1190                  new InputStreamReader(in, getCharacterEncoding()) :
1191                  new InputStreamReader(in));
1192             dataLength = reader.read(data);
1193             reader.close();
1194             inputAvail = true;
1195         } catch(IOException e) {
1196             // inputAvail will not be set
1197         }
1198 
1199         // Report the errors
1200         for (ErrorMessage msg = errors ; msg != null ; msg = msg.next) {
1201             // There used to be code here which checked
1202             // for duplicate error messages.  This functionality
1203             // has been moved to the method insertError().  See
1204             // the comments on that method for more information.
1205 
1206             int ln = (int) (msg.where >>> WHEREOFFSETBITS);
1207             int off = (int) (msg.where & ((1L << WHEREOFFSETBITS) - 1));
1208             if (off > dataLength)  off = dataLength;
1209 
1210             String referenceString = "";
1211             String markerString = "";
1212             if(inputAvail) {
1213                 int i, j;
1214                 for (i = off ; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r') ; i--);
1215                 for (j = off ; (j < dataLength) && (data[j] != '\n') && (data[j] != '\r') ; j++);
1216                 referenceString = new String(data, i, j - i);
1217 
1218                 char strdata[] = new char[(off - i) + 1];
1219                 for (j = i ; j < off ; j++) {
1220                     strdata[j-i] = (data[j] == '\t') ? '\t' : ' ';
1221                 }
1222                 strdata[off-i] = '^';
1223                 markerString = new String(strdata);
1224             }
1225 
1226             errorConsumer.pushError(errorFileName, ln, msg.message,
1227                                         referenceString, markerString);
1228         }
1229         errors = null;
1230     }
1231 
1232     /**
1233      * Report error
1234      */
1235     public
1236     void reportError(Object src, long where, String err, String msg) {
1237         if (src == null) {
1238             if (errorFileName != null) {
1239                 flushErrors();
1240                 errorFileName = null;
1241             }
1242             if (err.startsWith("warn.")) {
1243                 if (warnings()) {
1244                     nwarnings++;
1245                     output(msg);
1246                 }
1247                 return;
1248             }
1249             output("error: " + msg);
1250             nerrors++;
1251             flags |= F_ERRORSREPORTED;
1252 
1253         } else if (src instanceof String) {
1254             String fileName = (String)src;
1255 
1256             // Flush errors if we've moved on to a new file.
1257             if (!fileName.equals(errorFileName)) {
1258                 flushErrors();
1259                 errorFileName = fileName;
1260             }
1261 
1262             // Classify `err' as a warning, deprecation warning, or
1263             // error message.  Proceed accordingly.
1264             if (err.startsWith("warn.")) {
1265                 if (err.indexOf("is.deprecated") >= 0) {
1266                     // This is a deprecation warning.  Add `src' to the
1267                     // list of files with deprecation warnings.
1268                     if (!deprecationFiles.contains(src)) {
1269                         deprecationFiles.addElement(src);
1270                     }
1271 
1272                     // If we are reporting deprecations, try to add it
1273                     // to our list.  Otherwise, just increment the
1274                     // deprecation count.
1275                     if (deprecation()) {
1276                         if (insertError(where, msg)) {
1277                             ndeprecations++;
1278                         }
1279                     } else {
1280                         ndeprecations++;
1281                     }
1282                 } else {
1283                     // This is a regular warning.  If we are reporting
1284                     // warnings, try to add it to the list.  Otherwise, just
1285                     // increment the warning count.
1286                     if (warnings()) {
1287                         if (insertError(where, msg)) {
1288                             nwarnings++;
1289                         }
1290                     } else {
1291                         nwarnings++;
1292                     }
1293                 }
1294             } else {
1295                 // This is an error.  Try to add it to the list of errors.
1296                 // If it isn't a duplicate, increment our error count.
1297                 if (insertError(where, msg)) {
1298                     nerrors++;
1299                     flags |= F_ERRORSREPORTED;
1300                 }
1301             }
1302         } else if (src instanceof ClassFile) {
1303             reportError(((ClassFile)src).getPath(), where, err, msg);
1304 
1305         } else if (src instanceof Identifier) {
1306             reportError(src.toString(), where, err, msg);
1307 
1308         } else if (src instanceof ClassDeclaration) {
1309             try {
1310                 reportError(((ClassDeclaration)src).getClassDefinition(this), where, err, msg);
1311             } catch (ClassNotFound e) {
1312                 reportError(((ClassDeclaration)src).getName(), where, err, msg);
1313             }
1314         } else if (src instanceof ClassDefinition) {
1315             ClassDefinition c = (ClassDefinition)src;
1316             if (!err.startsWith("warn.")) {
1317                 c.setError();
1318             }
1319             reportError(c.getSource(), where, err, msg);
1320 
1321         } else if (src instanceof MemberDefinition) {
1322             reportError(((MemberDefinition)src).getClassDeclaration(), where, err, msg);
1323 
1324         } else {
1325             output(src + ":error=" + err + ":" + msg);
1326         }
1327     }
1328 
1329     /**
1330      * Issue an error
1331      */
1332     public void error(Object source, long where, String err, Object arg1, Object arg2, Object arg3) {
1333         if (errorsPushed >= errorLimit + nwarnings) {
1334             // Don't bother to queue any more errors if they won't get printed.
1335             return;
1336         }
1337         if (System.getProperty("javac.dump.stack") != null) {
1338             output("javac.err."+err+": "+errorString(err, arg1, arg2, arg3));
1339             new Exception("Stack trace").printStackTrace(new PrintStream(out));
1340         }
1341         reportError(source, where, err, errorString(err, arg1, arg2, arg3));
1342     }
1343 
1344     /**
1345      * Output a string. This can either be an error message or something
1346      * for debugging.
1347      */
1348     public void output(String msg) {
1349         PrintStream out =
1350             this.out instanceof PrintStream ? (PrintStream)this.out
1351                                             : new PrintStream(this.out, true);
1352         out.println(msg);
1353     }
1354 }