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 }