|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.javac; |
|
|
|
import sun.tools.java.*; |
|
import sun.tools.tree.Node; |
|
import sun.tools.java.Package; |
|
|
|
import java.util.*; |
|
import java.io.*; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated |
|
public |
|
class BatchEnvironment extends Environment implements ErrorConsumer { |
|
|
|
|
|
*/ |
|
OutputStream out; |
|
|
|
|
|
|
|
*/ |
|
protected ClassPath sourcePath; |
|
|
|
|
|
|
|
*/ |
|
protected ClassPath binaryPath; |
|
|
|
|
|
|
|
*/ |
|
Hashtable packages = new Hashtable(31); |
|
|
|
|
|
|
|
*/ |
|
Vector classesOrdered = new Vector(); |
|
|
|
|
|
|
|
*/ |
|
Hashtable classes = new Hashtable(351); |
|
|
|
|
|
|
|
*/ |
|
public int flags; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public short majorVersion = JAVA_DEFAULT_VERSION; |
|
public short minorVersion = JAVA_DEFAULT_MINOR_VERSION; |
|
|
|
// JCOV |
|
|
|
|
|
*/ |
|
public File covFile; |
|
// end JCOV |
|
|
|
|
|
|
|
*/ |
|
public int nerrors; |
|
public int nwarnings; |
|
public int ndeprecations; |
|
|
|
|
|
|
|
*/ |
|
Vector deprecationFiles = new Vector(); |
|
|
|
/** |
|
* writes out error messages |
|
*/ |
|
|
|
ErrorConsumer errorConsumer; |
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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) { |
|
|
|
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, |
|
|
|
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) { |
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public int getFlags() { |
|
return flags; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public short getMajorVersion() { |
|
return majorVersion; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public short getMinorVersion() { |
|
return minorVersion; |
|
} |
|
|
|
// JCOV |
|
|
|
|
|
*/ |
|
public File getcovFile() { |
|
return covFile; |
|
} |
|
// end JCOV |
|
|
|
|
|
|
|
|
|
*/ |
|
public Enumeration getClasses() { |
|
return classesOrdered.elements(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Set exemptPackages; |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isExemptPackage(Identifier id) { |
|
if (exemptPackages == null) { |
|
// Collect a list of the packages of all classes currently |
|
|
|
setExemptPackages(); |
|
} |
|
|
|
return exemptPackages.contains(id); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
|
|
|
|
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 |
|
|
|
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. |
|
|
|
|
|
if (!exemptPackages.contains(idJavaLang)) { |
|
|
|
exemptPackages.add(idJavaLang); |
|
|
|
try { |
|
if (!getPackage(idJavaLang).exists()) { |
|
|
|
error(0, "package.not.found.strong", idJavaLang); |
|
return; |
|
} |
|
} catch (IOException ee) { |
|
// We got an IO exception checking to see if the package |
|
|
|
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; |
|
// } |
|
} |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean classExists(Identifier nm) { |
|
if (nm.isInner()) { |
|
nm = nm.getTopName(); |
|
} |
|
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; |
|
} |
|
} |
|
*--------------------------*/ |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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(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 |
|
|
|
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). |
|
// |
|
|
|
Enumeration e = p.classes.elements(); |
|
|
|
|
|
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(); |
|
|
|
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 |
|
|
|
if (current != first) { |
|
current.addDependency(first.getClassDeclaration()); |
|
first.addDependency(current.getClassDeclaration()); |
|
} |
|
} |
|
|
|
if (tracing) dtExit("parseFile: SOURCE PARSED " + 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 |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
int loadFileFlags() { |
|
return 0; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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)) { |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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) { |
|
|
|
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 |
|
|
|
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) { |
|
|
|
c.setDefinition(null, CS_NOTFOUND); |
|
if (tracing) |
|
dtExit("loadDefinition: NOT FOUND (source or binary)"); |
|
return; |
|
} |
|
|
|
|
|
if (bc.getSource() != null) { |
|
srcfile = new ClassFile(new File((String)bc.getSource())); |
|
|
|
srcfile = pkg.getSourceFile(srcfile.getName()); |
|
if ((srcfile != null) && srcfile.exists()) { |
|
if (tracing) |
|
dtEvent("loadDefinition: FILENAME IN BINARY " + |
|
srcfile); |
|
if (srcfile.lastModified() > binfile.lastModified()) { |
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
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()) { |
|
|
|
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)) { |
|
|
|
for (Enumeration e = tab.keys() ; e.hasMoreElements() ; ) { |
|
ClassDeclaration dep = (ClassDeclaration)e.nextElement(); |
|
if (dep.getStatus() == CS_UNDECIDED) { |
|
|
|
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) { |
|
|
|
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 { |
|
|
|
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) { |
|
|
|
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)) { |
|
|
|
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()); |
|
} |
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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 '$'. |
|
|
|
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 |
|
|
|
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)); |
|
|
|
if ((modifiers & M_ANONYMOUS) != 0) { |
|
localName = idNull; |
|
} else { |
|
|
|
localName = nm; |
|
} |
|
} else if (outerClass != null) { |
|
|
|
pkgNm = Identifier.lookupInner(outerClass.getName(), nm); |
|
} else { |
|
pkgNm = nm; |
|
} |
|
|
|
|
|
ClassDeclaration c = toplevelEnv.getClassDeclaration(pkgNm); |
|
|
|
|
|
if (c.isDefined()) { |
|
toplevelEnv.error(nmpos, "class.multidef", |
|
c.getName(), c.getClassDefinition().getSource()); |
|
|
|
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) { |
|
|
|
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'. |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
|
|
|
|
|
|
*/ |
|
String errorFileName; |
|
|
|
|
|
|
|
*/ |
|
ErrorMessage errors; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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 |
|
|
|
return false; |
|
|
|
} else { |
|
// Okay, we know that the error doesn't come first. Walk |
|
|
|
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 |
|
|
|
while ((next = current.next) != null |
|
&& next.where == where) { |
|
if (next.message.equals(message)) { |
|
// We have found an exact duplicate. Don't bother to |
|
|
|
return false; |
|
} |
|
current = next; |
|
} |
|
|
|
|
|
ErrorMessage newMsg = new ErrorMessage(where, message); |
|
newMsg.next = current.next; |
|
current.next = newMsg; |
|
} |
|
|
|
|
|
return true; |
|
} |
|
|
|
private int errorsPushed; |
|
|
|
|
|
|
|
*/ |
|
public int errorLimit = 100; |
|
|
|
private boolean hitErrorLimit; |
|
|
|
/** |
|
* 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 |
|
|
|
output(errorFileName + ": " + message); |
|
} |
|
} |
|
|
|
public void flushErrors() { |
|
if (errors == null) { |
|
return; |
|
} |
|
|
|
boolean inputAvail = false; |
|
|
|
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, |
|
|
|
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 |
|
} |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
if (!fileName.equals(errorFileName)) { |
|
flushErrors(); |
|
errorFileName = fileName; |
|
} |
|
|
|
// Classify `err' as a warning, deprecation warning, or |
|
|
|
if (err.startsWith("warn.")) { |
|
if (err.indexOf("is.deprecated") >= 0) { |
|
// This is a deprecation warning. Add `src' to the |
|
|
|
if (!deprecationFiles.contains(src)) { |
|
deprecationFiles.addElement(src); |
|
} |
|
|
|
// If we are reporting deprecations, try to add it |
|
// to our list. Otherwise, just increment the |
|
|
|
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 |
|
|
|
if (warnings()) { |
|
if (insertError(where, msg)) { |
|
nwarnings++; |
|
} |
|
} else { |
|
nwarnings++; |
|
} |
|
} |
|
} else { |
|
// This is an error. Try to add it to the list of errors. |
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void error(Object source, long where, String err, Object arg1, Object arg2, Object arg3) { |
|
if (errorsPushed >= errorLimit + nwarnings) { |
|
|
|
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)); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void output(String msg) { |
|
PrintStream out = |
|
this.out instanceof PrintStream ? (PrintStream)this.out |
|
: new PrintStream(this.out, true); |
|
out.println(msg); |
|
} |
|
} |