/*
 * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.tools.javac;

import sun.tools.java.*;
import sun.tools.tree.Node;
import sun.tools.java.Package;

import java.util.*;
import java.io.*;

Main environment of the batch version of the Java compiler, this needs more work. WARNING: The contents of this source file are not part of any supported API. Code that depends on them does so at its own risk: they are subject to change or removal without notice.
/** * Main environment of the batch version of the Java compiler, * this needs more work. * * WARNING: The contents of this source file are not part of any * supported API. Code that depends on them does so at its own risk: * they are subject to change or removal without notice. */
@Deprecated public class BatchEnvironment extends Environment implements ErrorConsumer {
The stream where error message are printed.
/** * The stream where error message are printed. */
OutputStream out;
The path we use for finding source files.
/** * The path we use for finding source files. */
protected ClassPath sourcePath;
The path we use for finding class (binary) files.
/** * The path we use for finding class (binary) files. */
protected ClassPath binaryPath;
A hashtable of resource contexts.
/** * A hashtable of resource contexts. */
Hashtable packages = new Hashtable(31);
The classes, in order of appearance.
/** * The classes, in order of appearance. */
Vector classesOrdered = new Vector();
The classes, keyed by ClassDeclaration.
/** * The classes, keyed by ClassDeclaration. */
Hashtable classes = new Hashtable(351);
flags
/** * flags */
public int flags;
Major and minor versions to use for generated class files. Environments that extend BatchEnvironment (such as javadoc's Env class) get the default values below. javac itself may override these versions with values determined from the command line "-target" option.
/** * Major and minor versions to use for generated class files. * Environments that extend BatchEnvironment (such as javadoc's * Env class) get the default values below. * * javac itself may override these versions with values determined * from the command line "-target" option. */
public short majorVersion = JAVA_DEFAULT_VERSION; public short minorVersion = JAVA_DEFAULT_MINOR_VERSION; // JCOV
coverage data file
/** * coverage data file */
public File covFile; // end JCOV
The number of errors and warnings
/** * The number of errors and warnings */
public int nerrors; public int nwarnings; public int ndeprecations;
A list of files containing deprecation warnings.
/** * A list of files containing deprecation warnings. */
Vector deprecationFiles = new Vector();
writes out error messages
/** * writes out error messages */
ErrorConsumer errorConsumer;
Old constructors -- these constructors build a BatchEnvironment with an old-style class path.
/** * Old constructors -- these constructors build a BatchEnvironment * with an old-style class path. */
public BatchEnvironment(ClassPath path) { this(System.out, path); } public BatchEnvironment(OutputStream out, ClassPath path) { this(out, path, (ErrorConsumer) null); } public BatchEnvironment(OutputStream out, ClassPath path, ErrorConsumer errorConsumer) { this(out, path, path, errorConsumer); }
New constructors -- these constructors build a BatchEnvironment with a source path and a binary path.
/** * New constructors -- these constructors build a BatchEnvironment * with a source path and a binary path. */
public BatchEnvironment(ClassPath sourcePath, ClassPath binaryPath) { this(System.out, sourcePath, binaryPath); } public BatchEnvironment(OutputStream out, ClassPath sourcePath, ClassPath binaryPath) { this(out, sourcePath, binaryPath, (ErrorConsumer) null); } public BatchEnvironment(OutputStream out, ClassPath sourcePath, ClassPath binaryPath, ErrorConsumer errorConsumer) { this.out = out; this.sourcePath = sourcePath; this.binaryPath = binaryPath; this.errorConsumer = (errorConsumer == null) ? this : errorConsumer; }
Factory
/** * Factory */
static BatchEnvironment create(OutputStream out, String srcPathString, String classPathString, String sysClassPathString, String extDirsString){ ClassPath[] classPaths = classPaths(srcPathString, classPathString, sysClassPathString, extDirsString); return new BatchEnvironment(out, classPaths[0], classPaths[1]); } protected static ClassPath[] classPaths(String srcPathString, String classPathString, String sysClassPathString, String extDirsString) { // Create our source classpath and our binary classpath ClassPath sourcePath; ClassPath binaryPath; StringBuffer binaryPathBuffer = new StringBuffer(); if (classPathString == null) { // The env.class.path property is the user's CLASSPATH // environment variable, and it set by the wrapper (ie, // javac.exe). classPathString = System.getProperty("env.class.path"); if (classPathString == null) { classPathString = "."; } } if (srcPathString == null) { srcPathString = classPathString; } if (sysClassPathString == null) { sysClassPathString = System.getProperty("sun.boot.class.path"); if (sysClassPathString == null) { // shouldn't happen; recover gracefully sysClassPathString = classPathString; } } appendPath(binaryPathBuffer, sysClassPathString); if (extDirsString == null) { extDirsString = System.getProperty("java.ext.dirs"); } if (extDirsString != null) { StringTokenizer st = new StringTokenizer(extDirsString, File.pathSeparator); while (st.hasMoreTokens()) { String dirName = st.nextToken(); File dir = new File(dirName); if (!dirName.endsWith(File.separator)) { dirName += File.separator; } if (dir.isDirectory()) { String[] files = dir.list(); for (int i = 0; i < files.length; ++i) { String name = files[i]; if (name.endsWith(".jar")) { appendPath(binaryPathBuffer, dirName + name); } } } } } appendPath(binaryPathBuffer, classPathString); sourcePath = new ClassPath(srcPathString); binaryPath = new ClassPath(binaryPathBuffer.toString()); return new ClassPath[]{sourcePath, binaryPath}; } private static void appendPath(StringBuffer buf, String str) { if (str.length() > 0) { if (buf.length() > 0) { buf.append(File.pathSeparator); } buf.append(str); } }
Return flags
/** * Return flags */
public int getFlags() { return flags; }
Return major version to use for generated class files
/** * Return major version to use for generated class files */
public short getMajorVersion() { return majorVersion; }
Return minor version to use for generated class files
/** * Return minor version to use for generated class files */
public short getMinorVersion() { return minorVersion; } // JCOV
Return coverage data file
/** * Return coverage data file */
public File getcovFile() { return covFile; } // end JCOV
Return an enumeration of all the currently defined classes in order of appearance to getClassDeclaration().
/** * Return an enumeration of all the currently defined classes * in order of appearance to getClassDeclaration(). */
public Enumeration getClasses() { return classesOrdered.elements(); }
A set of Identifiers for all packages exempt from the "exists" check in Imports#resolve(). These are the current packages for all classes being compiled as of the first call to isExemptPackage.
/** * A set of Identifiers for all packages exempt from the "exists" * check in Imports#resolve(). These are the current packages for * all classes being compiled as of the first call to isExemptPackage. */
private Set exemptPackages;
Tells whether an Identifier refers to a package which should be exempt from the "exists" check in Imports#resolve().
/** * Tells whether an Identifier refers to a package which should be * exempt from the "exists" check in Imports#resolve(). */
public boolean isExemptPackage(Identifier id) { if (exemptPackages == null) { // Collect a list of the packages of all classes currently // being compiled. setExemptPackages(); } return exemptPackages.contains(id); }
Set the set of packages which are exempt from the exists check in Imports#resolve().
/** * Set the set of packages which are exempt from the exists check * in Imports#resolve(). */
private void setExemptPackages() { // The JLS gives us the freedom to define "accessibility" of // a package in whatever manner we wish. After the evaluation // of bug 4093217, we have decided to consider a package P // accessible if either: // // 1. The directory corresponding to P exists on the classpath. // 2. For any class C currently being compiled, C belongs to // package P. // 3. For any class C currently being compiled, C belongs to // package Q and Q is a subpackage of P. // // In order to implement this, we collect the current packages // (and prefixes) of all packages we have found so far. These // will be exempt from the "exists" check in // sun.tools.java.Imports#resolve(). exemptPackages = new HashSet(101); // Add all of the current packages and their prefixes to our set. for (Enumeration e = getClasses(); e.hasMoreElements(); ) { ClassDeclaration c = (ClassDeclaration) e.nextElement(); if (c.getStatus() == CS_PARSED) { SourceClass def = (SourceClass) c.getClassDefinition(); if (def.isLocal()) continue; Identifier pkg = def.getImports().getCurrentPackage(); // Add the name of this package and all of its prefixes // to our set. while (pkg != idNull && exemptPackages.add(pkg)) { pkg = pkg.getQualifier(); } } } // Before we go any further, we make sure java.lang is // accessible and that it is not ambiguous. These checks // are performed for "ordinary" packages in // sun.tools.java.Imports#resolve(). The reason we perform // them specially for java.lang is that we want to report // the error once, and outside of any particular file. // Check to see if java.lang is accessible. if (!exemptPackages.contains(idJavaLang)) { // Add java.lang to the set of exempt packages. exemptPackages.add(idJavaLang); try { if (!getPackage(idJavaLang).exists()) { // java.lang doesn't exist. error(0, "package.not.found.strong", idJavaLang); return; } } catch (IOException ee) { // We got an IO exception checking to see if the package // java.lang exists. error(0, "io.exception.package", idJavaLang); } } // Next we ensure that java.lang is not both a class and // a package. (Fix for 4101529) // // This change has been backed out because, on WIN32, it // failed to take character case into account. It will // be put back in later. // // Identifier resolvedName = // resolvePackageQualifiedName(idJavaLang); // Identifier topClassName = resolvedName.getTopName(); // //if (Imports.importable(topClassName, env)) { // if (Imports.importable(topClassName, this)) { // // It is a package and a class. Emit the error. // error(0, "package.class.conflict.strong", // idJavaLang, topClassName); // return; // } }
Get a class, given the fully qualified class name
/** * Get a class, given the fully qualified class name */
public ClassDeclaration getClassDeclaration(Identifier nm) { return getClassDeclaration(Type.tClass(nm)); } public ClassDeclaration getClassDeclaration(Type t) { ClassDeclaration c = (ClassDeclaration)classes.get(t); if (c == null) { classes.put(t, c = new ClassDeclaration(t.getClassName())); classesOrdered.addElement(c); } return c; }
Check if a class exists Applies only to package members (non-nested classes).
/** * Check if a class exists * Applies only to package members (non-nested classes). */
public boolean classExists(Identifier nm) { if (nm.isInner()) { nm = nm.getTopName(); // just in case } Type t = Type.tClass(nm); try { ClassDeclaration c = (ClassDeclaration)classes.get(t); return (c != null) ? c.getName().equals(nm) : getPackage(nm.getQualifier()).classExists(nm.getName()); } catch (IOException e) { return true; } } /** * Generate a new name similar to the given one. * Do it in such a way that repeated compilations of * the same source generate the same series of names. */ // This code does not perform as stated above. // Correction below is part of fix for bug id 4056065. // // NOTE: The method 'generateName' has now been folded into its // single caller, 'makeClassDefinition', which appears later in // this file. /*--------------------------* public Identifier generateName(ClassDefinition outerClass, Identifier nm) { Identifier outerNm = outerClass.getName(); Identifier flat = outerNm.getFlatName(); Identifier stem = Identifier.lookup(outerNm.getQualifier(), flat.getHead()); for (int i = 1; ; i++) { String name = i + (nm.equals(idNull) ? "" : SIG_INNERCLASS + nm); Identifier nm1 = Identifier.lookupInner(stem, Identifier.lookup(name)); if (classes.get(Type.tClass(nm1)) == null) return nm1; } } *--------------------------*/
Get the package path for a package
/** * Get the package path for a package */
public Package getPackage(Identifier pkg) throws IOException { Package p = (Package)packages.get(pkg); if (p == null) { packages.put(pkg, p = new Package(sourcePath, binaryPath, pkg)); } return p; }
Parse a source file
/** * Parse a source file */
public void parseFile(ClassFile file) throws FileNotFoundException { long tm = System.currentTimeMillis(); InputStream input; BatchParser p; if (tracing) dtEnter("parseFile: PARSING SOURCE " + file); Environment env = new Environment(this, file); try { input = file.getInputStream(); env.setCharacterEncoding(getCharacterEncoding()); // p = new BatchParser(e, new BufferedInputStream(input)); p = new BatchParser(env, input); } catch(IOException ex) { if (tracing) dtEvent("parseFile: IO EXCEPTION " + file); throw new FileNotFoundException(); } try { p.parseFile(); } catch(Exception e) { throw new CompilerError(e); } try { input.close(); } catch (IOException ex) { // We're turn with the input, so ignore this. } if (verbose()) { tm = System.currentTimeMillis() - tm; output(Main.getText("benv.parsed_in", file.getPath(), Long.toString(tm))); } if (p.classes.size() == 0) { // The JLS allows a file to contain no compilation units -- // that is, it allows a file to contain no classes or interfaces. // In this case, we are still responsible for checking that the // imports resolve properly. The way the compiler is organized, // this is the last point at which we still have enough information // to do so. (Fix for 4041851). p.imports.resolve(env); } else { // In an attempt to see that classes which come from the // same source file are all recompiled when any one of them // would be recompiled (when using the -depend option) we // introduce artificial dependencies between these classes. // We do this by calling the addDependency() method, which // adds a (potentially unused) class reference to the constant // pool of the class. // // Previously, we added a dependency from every class in the // file, to every class in the file. This introduced, in // total, a quadratic number of potentially bogus constant // pool entries. This was bad. Now we add our artificial // dependencies in such a way that the classes are connected // in a circle. While single links is probably sufficient, the // code below adds double links just to be diligent. // (Fix for 4108286). // // Note that we don't chain in inner classes. The links // between them and their outerclass should be sufficient // here. // (Fix for 4107960). // // The dependency code was previously in BatchParser.java. Enumeration e = p.classes.elements(); // first will not be an inner class. ClassDefinition first = (ClassDefinition) e.nextElement(); if (first.isInnerClass()) { throw new CompilerError("BatchEnvironment, first is inner"); } ClassDefinition current = first; ClassDefinition next; while (e.hasMoreElements()) { next = (ClassDefinition) e.nextElement(); // Don't chain in inner classes. if (next.isInnerClass()) { continue; } current.addDependency(next.getClassDeclaration()); next.addDependency(current.getClassDeclaration()); current = next; } // Make a circle. Don't bother to add a dependency if there // is only one class in the file. if (current != first) { current.addDependency(first.getClassDeclaration()); first.addDependency(current.getClassDeclaration()); } } if (tracing) dtExit("parseFile: SOURCE PARSED " + file); }
Load a binary file
/** * Load a binary file */
BinaryClass loadFile(ClassFile file) throws IOException { long tm = System.currentTimeMillis(); InputStream input = file.getInputStream(); BinaryClass c = null; if (tracing) dtEnter("loadFile: LOADING CLASSFILE " + file); try { DataInputStream is = new DataInputStream(new BufferedInputStream(input)); c = BinaryClass.load(new Environment(this, file), is, loadFileFlags()); } catch (ClassFormatError e) { error(0, "class.format", file.getPath(), e.getMessage()); if (tracing) dtExit("loadFile: CLASS FORMAT ERROR " + file); return null; } catch (java.io.EOFException e) { // If we get an EOF while processing a class file, then // it has been truncated. We let other I/O errors pass // through. Fix for 4088443. error(0, "truncated.class", file.getPath()); return null; } input.close(); if (verbose()) { tm = System.currentTimeMillis() - tm; output(Main.getText("benv.loaded_in", file.getPath(), Long.toString(tm))); } if (tracing) dtExit("loadFile: CLASSFILE LOADED " + file); return c; }
Default flags for loadFile. Subclasses may override this.
/** * Default flags for loadFile. Subclasses may override this. */
int loadFileFlags() { return 0; }
Load a binary class
/** * Load a binary class */
boolean needsCompilation(Hashtable check, ClassDeclaration c) { switch (c.getStatus()) { case CS_UNDEFINED: if (tracing) dtEnter("needsCompilation: UNDEFINED " + c.getName()); loadDefinition(c); return needsCompilation(check, c); case CS_UNDECIDED: if (tracing) dtEnter("needsCompilation: UNDECIDED " + c.getName()); if (check.get(c) == null) { check.put(c, c); BinaryClass bin = (BinaryClass)c.getClassDefinition(); for (Enumeration e = bin.getDependencies() ; e.hasMoreElements() ;) { ClassDeclaration dep = (ClassDeclaration)e.nextElement(); if (needsCompilation(check, dep)) { // It must be source, dependencies need compilation c.setDefinition(bin, CS_SOURCE); if (tracing) dtExit("needsCompilation: YES (source) " + c.getName()); return true; } } } if (tracing) dtExit("needsCompilation: NO (undecided) " + c.getName()); return false; case CS_BINARY: if (tracing) { dtEnter("needsCompilation: BINARY " + c.getName()); dtExit("needsCompilation: NO (binary) " + c.getName()); } return false; } if (tracing) dtExit("needsCompilation: YES " + c.getName()); return true; }
Load the definition of a class or at least determine how to load it. The caller must repeat calls to this method until it the state converges to CS_BINARY, CS_PARSED, or the like..
See Also:
  • getClassDefinition.getClassDefinition
/** * Load the definition of a class * or at least determine how to load it. * The caller must repeat calls to this method * until it the state converges to CS_BINARY, CS_PARSED, or the like.. * @see ClassDeclaration#getClassDefinition */
public void loadDefinition(ClassDeclaration c) { if (tracing) dtEnter("loadDefinition: ENTER " + c.getName() + ", status " + c.getStatus()); switch (c.getStatus()) { case CS_UNDEFINED: { if (tracing) dtEvent("loadDefinition: STATUS IS UNDEFINED"); Identifier nm = c.getName(); Package pkg; try { pkg = getPackage(nm.getQualifier()); } catch (IOException e) { // If we can't get at the package, then we'll just // have to set the class to be not found. c.setDefinition(null, CS_NOTFOUND); error(0, "io.exception", c); if (tracing) dtExit("loadDefinition: IO EXCEPTION (package)"); return; } ClassFile binfile = pkg.getBinaryFile(nm.getName()); if (binfile == null) { // must be source, there is no binary c.setDefinition(null, CS_SOURCE); if (tracing) dtExit("loadDefinition: MUST BE SOURCE (no binary) " + c.getName()); return; } ClassFile srcfile = pkg.getSourceFile(nm.getName()); if (srcfile == null) { if (tracing) dtEvent("loadDefinition: NO SOURCE " + c.getName()); BinaryClass bc = null; try { bc = loadFile(binfile); } catch (IOException e) { // If we can't access the binary, set the class to // be not found. (bug id 4030497) c.setDefinition(null, CS_NOTFOUND); error(0, "io.exception", binfile); if (tracing) dtExit("loadDefinition: IO EXCEPTION (binary)"); return; } if ((bc != null) && !bc.getName().equals(nm)) { error(0, "wrong.class", binfile.getPath(), c, bc); bc = null; if (tracing) dtEvent("loadDefinition: WRONG CLASS (binary)"); } if (bc == null) { // no source nor binary found c.setDefinition(null, CS_NOTFOUND); if (tracing) dtExit("loadDefinition: NOT FOUND (source or binary)"); return; } // Couldn't find the source, try the one mentioned in the binary if (bc.getSource() != null) { srcfile = new ClassFile(new File((String)bc.getSource())); // Look for the source file srcfile = pkg.getSourceFile(srcfile.getName()); if ((srcfile != null) && srcfile.exists()) { if (tracing) dtEvent("loadDefinition: FILENAME IN BINARY " + srcfile); if (srcfile.lastModified() > binfile.lastModified()) { // must be source, it is newer than the binary c.setDefinition(bc, CS_SOURCE); if (tracing) dtEvent("loadDefinition: SOURCE IS NEWER " + srcfile); bc.loadNested(this); if (tracing) dtExit("loadDefinition: MUST BE SOURCE " + c.getName()); return; } if (dependencies()) { c.setDefinition(bc, CS_UNDECIDED); if (tracing) dtEvent("loadDefinition: UNDECIDED " + c.getName()); } else { c.setDefinition(bc, CS_BINARY); if (tracing) dtEvent("loadDefinition: MUST BE BINARY " + c.getName()); } bc.loadNested(this); if (tracing) dtExit("loadDefinition: EXIT " + c.getName() + ", status " + c.getStatus()); return; } } // It must be binary, there is no source c.setDefinition(bc, CS_BINARY); if (tracing) dtEvent("loadDefinition: MUST BE BINARY (no source) " + c.getName()); bc.loadNested(this); if (tracing) dtExit("loadDefinition: EXIT " + c.getName() + ", status " + c.getStatus()); return; } BinaryClass bc = null; try { if (srcfile.lastModified() > binfile.lastModified()) { // must be source, it is newer than the binary c.setDefinition(null, CS_SOURCE); if (tracing) dtEvent("loadDefinition: MUST BE SOURCE (younger than binary) " + c.getName()); return; } bc = loadFile(binfile); } catch (IOException e) { error(0, "io.exception", binfile); if (tracing) dtEvent("loadDefinition: IO EXCEPTION (binary)"); } if ((bc != null) && !bc.getName().equals(nm)) { error(0, "wrong.class", binfile.getPath(), c, bc); bc = null; if (tracing) dtEvent("loadDefinition: WRONG CLASS (binary)"); } if (bc != null) { Identifier name = bc.getName(); if (name.equals(c.getName())) { if (dependencies()) { c.setDefinition(bc, CS_UNDECIDED); if (tracing) dtEvent("loadDefinition: UNDECIDED " + name); } else { c.setDefinition(bc, CS_BINARY); if (tracing) dtEvent("loadDefinition: MUST BE BINARY " + name); } } else { c.setDefinition(null, CS_NOTFOUND); if (tracing) dtEvent("loadDefinition: NOT FOUND (source or binary)"); if (dependencies()) { getClassDeclaration(name).setDefinition(bc, CS_UNDECIDED); if (tracing) dtEvent("loadDefinition: UNDECIDED " + name); } else { getClassDeclaration(name).setDefinition(bc, CS_BINARY); if (tracing) dtEvent("loadDefinition: MUST BE BINARY " + name); } } } else { c.setDefinition(null, CS_NOTFOUND); if (tracing) dtEvent("loadDefinition: NOT FOUND (source or binary)"); } if (bc != null && bc == c.getClassDefinition()) bc.loadNested(this); if (tracing) dtExit("loadDefinition: EXIT " + c.getName() + ", status " + c.getStatus()); return; } case CS_UNDECIDED: { if (tracing) dtEvent("loadDefinition: STATUS IS UNDECIDED"); Hashtable tab = new Hashtable(); if (!needsCompilation(tab, c)) { // All undecided classes that this class depends on must be binary for (Enumeration e = tab.keys() ; e.hasMoreElements() ; ) { ClassDeclaration dep = (ClassDeclaration)e.nextElement(); if (dep.getStatus() == CS_UNDECIDED) { // must be binary, dependencies need compilation dep.setDefinition(dep.getClassDefinition(), CS_BINARY); if (tracing) dtEvent("loadDefinition: MUST BE BINARY " + dep); } } } if (tracing) dtExit("loadDefinition: EXIT " + c.getName() + ", status " + c.getStatus()); return; } case CS_SOURCE: { if (tracing) dtEvent("loadDefinition: STATUS IS SOURCE"); ClassFile srcfile = null; Package pkg = null; if (c.getClassDefinition() != null) { // Use the source file name from the binary class file try { pkg = getPackage(c.getName().getQualifier()); srcfile = pkg.getSourceFile((String)c.getClassDefinition().getSource()); } catch (IOException e) { error(0, "io.exception", c); if (tracing) dtEvent("loadDefinition: IO EXCEPTION (package)"); } if (srcfile == null) { String fn = (String)c.getClassDefinition().getSource(); srcfile = new ClassFile(new File(fn)); } } else { // Get a source file name from the package Identifier nm = c.getName(); try { pkg = getPackage(nm.getQualifier()); srcfile = pkg.getSourceFile(nm.getName()); } catch (IOException e) { error(0, "io.exception", c); if (tracing) dtEvent("loadDefinition: IO EXCEPTION (package)"); } if (srcfile == null) { // not found, there is no source c.setDefinition(null, CS_NOTFOUND); if (tracing) dtExit("loadDefinition: SOURCE NOT FOUND " + c.getName() + ", status " + c.getStatus()); return; } } try { parseFile(srcfile); } catch (FileNotFoundException e) { error(0, "io.exception", srcfile); if (tracing) dtEvent("loadDefinition: IO EXCEPTION (source)"); } if ((c.getClassDefinition() == null) || (c.getStatus() == CS_SOURCE)) { // not found after parsing the file error(0, "wrong.source", srcfile.getPath(), c, pkg); c.setDefinition(null, CS_NOTFOUND); if (tracing) dtEvent("loadDefinition: WRONG CLASS (source) " + c.getName()); } if (tracing) dtExit("loadDefinition: EXIT " + c.getName() + ", status " + c.getStatus()); return; } } if (tracing) dtExit("loadDefinition: EXIT " + c.getName() + ", status " + c.getStatus()); }
Create a new class.
/** * Create a new class. */
public ClassDefinition makeClassDefinition(Environment toplevelEnv, long where, IdentifierToken name, String doc, int modifiers, IdentifierToken superClass, IdentifierToken interfaces[], ClassDefinition outerClass) { Identifier nm = name.getName(); long nmpos = name.getWhere(); Identifier pkgNm; String mangledName = null; ClassDefinition localContextClass = null; // Provide name for a local class. This used to be set after // the class was created, but it is needed for checking within // the class constructor. // NOTE: It seems that we could always provide the simple name, // and thereby avoid the test in 'ClassDefinition.getLocalName()' // for the definedness of the local name. There, if the local // name is not set, a simple name is extracted from the result of // 'getName()'. That name can potentially change, however, as // it is ultimately derived from 'ClassType.className', which is // set by 'Type.changeClassName'. Better leave this alone... Identifier localName = null; if (nm.isQualified() || nm.isInner()) { pkgNm = nm; } else if ((modifiers & (M_LOCAL | M_ANONYMOUS)) != 0) { // Inaccessible class. Create a name of the form // 'PackageMember.N$localName' or 'PackageMember.N'. // Note that the '.' will be converted later to a '$'. // pkgNm = generateName(outerClass, nm); localContextClass = outerClass.getTopClass(); // Always use the smallest number in generating the name that // renders the complete name unique within the top-level class. // This is required to make the names more predictable, as part // of a serialization-related workaround, and satisfies an obscure // requirement that the name of a local class be of the form // 'PackageMember$1$localName' when this name is unique. for (int i = 1 ; ; i++) { mangledName = i + (nm.equals(idNull) ? "" : SIG_INNERCLASS + nm); if (localContextClass.getLocalClass(mangledName) == null) { break; } } Identifier outerNm = localContextClass.getName(); pkgNm = Identifier.lookupInner(outerNm, Identifier.lookup(mangledName)); //System.out.println("LOCAL CLASS: " + pkgNm + " IN " + localContextClass); if ((modifiers & M_ANONYMOUS) != 0) { localName = idNull; } else { // Local class has a locally-scoped name which is independent of pkgNm. localName = nm; } } else if (outerClass != null) { // Accessible inner class. Qualify name with surrounding class name. pkgNm = Identifier.lookupInner(outerClass.getName(), nm); } else { pkgNm = nm; } // Find the class ClassDeclaration c = toplevelEnv.getClassDeclaration(pkgNm); // Make sure this is the first definition if (c.isDefined()) { toplevelEnv.error(nmpos, "class.multidef", c.getName(), c.getClassDefinition().getSource()); // Don't mess with the existing class declarations with same name c = new ClassDeclaration (pkgNm); } if (superClass == null && !pkgNm.equals(idJavaLangObject)) { superClass = new IdentifierToken(idJavaLangObject); } ClassDefinition sourceClass = new SourceClass(toplevelEnv, where, c, doc, modifiers, superClass, interfaces, (SourceClass) outerClass, localName); if (outerClass != null) { // It is a member of its enclosing class. outerClass.addMember(toplevelEnv, new SourceMember(sourceClass)); // Record local (or anonymous) class in the class whose name will // serve as the prefix of the local class name. This is necessary // so that the class may be retrieved from its name, which does not // fully represent the class nesting structure. // See 'ClassDefinition.getClassDefinition'. // This is part of a fix for bugid 4054523 and 4030421. if ((modifiers & (M_LOCAL | M_ANONYMOUS)) != 0) { localContextClass.addLocalClass(sourceClass, mangledName); } } // The local name of an anonymous or local class used to be set here // with a call to 'setLocalName'. This has been moved to the constructor // for 'SourceClass', which now takes a 'localName' argument. return sourceClass; }
Create a new field.
/** * Create a new field. */
public MemberDefinition makeMemberDefinition(Environment origEnv, long where, ClassDefinition clazz, String doc, int modifiers, Type type, Identifier name, IdentifierToken argNames[], IdentifierToken expIds[], Object value) { if (tracing) dtEvent("makeMemberDefinition: " + name + " IN " + clazz); Vector v = null; if (argNames != null) { v = new Vector(argNames.length); for (int i = 0 ; i < argNames.length ; i++) { v.addElement(argNames[i]); } } SourceMember f = new SourceMember(where, clazz, doc, modifiers, type, name, v, expIds, (Node)value); clazz.addMember(origEnv, f); return f; }
Release resources in classpath.
/** * Release resources in classpath. */
public void shutdown() { try { if (sourcePath != null) { sourcePath.close(); } if (binaryPath != null && binaryPath != sourcePath) { binaryPath.close(); } } catch (IOException ee) { output(Main.getText("benv.failed_to_close_class_path", ee.toString())); } sourcePath = null; binaryPath = null; super.shutdown(); }
Error String
/** * Error String */
public String errorString(String err, Object arg1, Object arg2, Object arg3) { String key = null; if(err.startsWith("warn.")) key = "javac.err." + err.substring(5); else key = "javac.err." + err; return Main.getText(key, arg1 != null ? arg1.toString() : null, arg2 != null ? arg2.toString() : null, arg3 != null ? arg3.toString() : null); }
The filename where the last errors have occurred
/** * The filename where the last errors have occurred */
String errorFileName;
List of outstanding error messages
/** * List of outstanding error messages */
ErrorMessage errors;
Insert an error message in the list of outstanding error messages. The list is sorted on input position and contains no duplicates. The return value indicates whether or not the message was actually inserted. The method flushErrors() used to check for duplicate error messages. It would only detect duplicates if they were contiguous. Removing non-contiguous duplicate error messages is slightly less complicated at insertion time, so the functionality was moved here. This also saves a miniscule number of allocations.
/** * Insert an error message in the list of outstanding error messages. * The list is sorted on input position and contains no duplicates. * The return value indicates whether or not the message was * actually inserted. * * The method flushErrors() used to check for duplicate error messages. * It would only detect duplicates if they were contiguous. Removing * non-contiguous duplicate error messages is slightly less complicated * at insertion time, so the functionality was moved here. This also * saves a miniscule number of allocations. */
protected boolean insertError(long where, String message) { //output("ERR = " + message); if (errors == null || errors.where > where) { // If the list is empty, or the error comes before any other // errors, insert it at the beginning of the list. ErrorMessage newMsg = new ErrorMessage(where, message); newMsg.next = errors; errors = newMsg; } else if (errors.where == where && errors.message.equals(message)) { // The new message is an exact duplicate of the first message // in the list. Don't insert it. return false; } else { // Okay, we know that the error doesn't come first. Walk // the list until we find the right position for insertion. ErrorMessage current = errors; ErrorMessage next; while ((next = current.next) != null && next.where < where) { current = next; } // Now walk over any errors with the same location, looking // for duplicates. If we find a duplicate, don't insert the // error. while ((next = current.next) != null && next.where == where) { if (next.message.equals(message)) { // We have found an exact duplicate. Don't bother to // insert the error. return false; } current = next; } // Now insert after current. ErrorMessage newMsg = new ErrorMessage(where, message); newMsg.next = current.next; current.next = newMsg; } // Indicate that the insertion occurred. return true; } private int errorsPushed;
Maximum number of errors to print.
/** * Maximum number of errors to print. */
public int errorLimit = 100; private boolean hitErrorLimit;
Flush outstanding errors
/** * Flush outstanding errors */
public void pushError(String errorFileName, int line, String message, String referenceText, String referenceTextPointer) { int limit = errorLimit + nwarnings; if (++errorsPushed >= limit && errorLimit >= 0) { if (!hitErrorLimit) { hitErrorLimit = true; output(errorString("too.many.errors", new Integer(errorLimit),null,null)); } return; } if (errorFileName.endsWith(".java")) { output(errorFileName + ":" + line + ": " + message); output(referenceText); output(referenceTextPointer); } else { // It wasn't really a source file (probably an error or // warning because of a malformed or badly versioned // class file. output(errorFileName + ": " + message); } } public void flushErrors() { if (errors == null) { return; } boolean inputAvail = false; // Read the file char data[] = null; int dataLength = 0; // A malformed file encoding could cause a CharConversionException. // If something bad happens while trying to find the source file, // don't bother trying to show lines. try { FileInputStream in = new FileInputStream(errorFileName); data = new char[in.available()]; InputStreamReader reader = (getCharacterEncoding() != null ? new InputStreamReader(in, getCharacterEncoding()) : new InputStreamReader(in)); dataLength = reader.read(data); reader.close(); inputAvail = true; } catch(IOException e) { // inputAvail will not be set } // Report the errors for (ErrorMessage msg = errors ; msg != null ; msg = msg.next) { // There used to be code here which checked // for duplicate error messages. This functionality // has been moved to the method insertError(). See // the comments on that method for more information. int ln = (int) (msg.where >>> WHEREOFFSETBITS); int off = (int) (msg.where & ((1L << WHEREOFFSETBITS) - 1)); if (off > dataLength) off = dataLength; String referenceString = ""; String markerString = ""; if(inputAvail) { int i, j; for (i = off ; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r') ; i--); for (j = off ; (j < dataLength) && (data[j] != '\n') && (data[j] != '\r') ; j++); referenceString = new String(data, i, j - i); char strdata[] = new char[(off - i) + 1]; for (j = i ; j < off ; j++) { strdata[j-i] = (data[j] == '\t') ? '\t' : ' '; } strdata[off-i] = '^'; markerString = new String(strdata); } errorConsumer.pushError(errorFileName, ln, msg.message, referenceString, markerString); } errors = null; }
Report error
/** * Report error */
public void reportError(Object src, long where, String err, String msg) { if (src == null) { if (errorFileName != null) { flushErrors(); errorFileName = null; } if (err.startsWith("warn.")) { if (warnings()) { nwarnings++; output(msg); } return; } output("error: " + msg); nerrors++; flags |= F_ERRORSREPORTED; } else if (src instanceof String) { String fileName = (String)src; // Flush errors if we've moved on to a new file. if (!fileName.equals(errorFileName)) { flushErrors(); errorFileName = fileName; } // Classify `err' as a warning, deprecation warning, or // error message. Proceed accordingly. if (err.startsWith("warn.")) { if (err.indexOf("is.deprecated") >= 0) { // This is a deprecation warning. Add `src' to the // list of files with deprecation warnings. if (!deprecationFiles.contains(src)) { deprecationFiles.addElement(src); } // If we are reporting deprecations, try to add it // to our list. Otherwise, just increment the // deprecation count. if (deprecation()) { if (insertError(where, msg)) { ndeprecations++; } } else { ndeprecations++; } } else { // This is a regular warning. If we are reporting // warnings, try to add it to the list. Otherwise, just // increment the warning count. if (warnings()) { if (insertError(where, msg)) { nwarnings++; } } else { nwarnings++; } } } else { // This is an error. Try to add it to the list of errors. // If it isn't a duplicate, increment our error count. if (insertError(where, msg)) { nerrors++; flags |= F_ERRORSREPORTED; } } } else if (src instanceof ClassFile) { reportError(((ClassFile)src).getPath(), where, err, msg); } else if (src instanceof Identifier) { reportError(src.toString(), where, err, msg); } else if (src instanceof ClassDeclaration) { try { reportError(((ClassDeclaration)src).getClassDefinition(this), where, err, msg); } catch (ClassNotFound e) { reportError(((ClassDeclaration)src).getName(), where, err, msg); } } else if (src instanceof ClassDefinition) { ClassDefinition c = (ClassDefinition)src; if (!err.startsWith("warn.")) { c.setError(); } reportError(c.getSource(), where, err, msg); } else if (src instanceof MemberDefinition) { reportError(((MemberDefinition)src).getClassDeclaration(), where, err, msg); } else { output(src + ":error=" + err + ":" + msg); } }
Issue an error
/** * Issue an error */
public void error(Object source, long where, String err, Object arg1, Object arg2, Object arg3) { if (errorsPushed >= errorLimit + nwarnings) { // Don't bother to queue any more errors if they won't get printed. return; } if (System.getProperty("javac.dump.stack") != null) { output("javac.err."+err+": "+errorString(err, arg1, arg2, arg3)); new Exception("Stack trace").printStackTrace(new PrintStream(out)); } reportError(source, where, err, errorString(err, arg1, arg2, arg3)); }
Output a string. This can either be an error message or something for debugging.
/** * Output a string. This can either be an error message or something * for debugging. */
public void output(String msg) { PrintStream out = this.out instanceof PrintStream ? (PrintStream)this.out : new PrintStream(this.out, true); out.println(msg); } }