|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.java; |
|
|
|
import java.util.Hashtable; |
|
import java.util.Vector; |
|
import java.util.Enumeration; |
|
import java.util.List; |
|
import java.util.Collections; |
|
import java.io.IOException; |
|
|
|
/** |
|
* This class describes the classes and packages imported |
|
* from a source file. A Hashtable called bindings is maintained |
|
* to quickly map symbol names to classes. This table is flushed |
|
* everytime a new import is added. |
|
* |
|
* A class name is resolved as follows: |
|
* - if it is a qualified name then return the corresponding class |
|
* - if the name corresponds to an individually imported class then return that class |
|
* - check if the class is defined in any of the imported packages, |
|
* if it is then return it, make sure it is defined in only one package |
|
* - assume that the class is defined in the current package |
|
* |
|
* 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. |
|
*/ |
|
|
|
public |
|
class Imports implements Constants { |
|
|
|
|
|
|
|
*/ |
|
Identifier currentPackage = idNull; |
|
|
|
|
|
|
|
|
|
*/ |
|
long currentPackageWhere = 0; |
|
|
|
|
|
|
|
*/ |
|
Hashtable classes = new Hashtable(); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Vector packages = new Vector(); |
|
|
|
|
|
|
|
|
|
*/ |
|
Vector singles = new Vector(); |
|
|
|
|
|
|
|
*/ |
|
protected int checked; |
|
|
|
|
|
|
|
*/ |
|
public Imports(Environment env) { |
|
addPackage(idJavaLang); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public synchronized void resolve(Environment env) { |
|
if (checked != 0) { |
|
return; |
|
} |
|
checked = -1; |
|
|
|
// After all class information has been read, now we can |
|
// safely inspect import information for errors. |
|
// If we did this before all parsing was finished, |
|
// we could get vicious circularities, since files can |
|
// import each others' classes. |
|
|
|
// A note: the resolution of the package java.lang takes place |
|
// in the sun.tools.javac.BatchEnvironment#setExemptPackages(). |
|
|
|
// Make sure that the current package's name does not collide |
|
// with the name of an existing class. (bug 4101529) |
|
// |
|
// This change has been backed out because, on WIN32, it |
|
// failed to distinguish between java.awt.event and |
|
// java.awt.Event when looking for a directory. We will |
|
// add this back in later. |
|
// |
|
// if (currentPackage != idNull) { |
|
// Identifier resolvedName = |
|
// env.resolvePackageQualifiedName(currentPackage); |
|
// |
|
// Identifier className = resolvedName.getTopName(); |
|
// |
|
// if (importable(className, env)) { |
|
// // The name of the current package is also the name |
|
// // of a class. |
|
// env.error(currentPackageWhere, "package.class.conflict", |
|
// currentPackage, className); |
|
// } |
|
// } |
|
|
|
Vector resolvedPackages = new Vector(); |
|
for (Enumeration e = packages.elements() ; e.hasMoreElements() ;) { |
|
IdentifierToken t = (IdentifierToken)e.nextElement(); |
|
Identifier nm = t.getName(); |
|
long where = t.getWhere(); |
|
|
|
// Check to see if this package is exempt from the "exists" |
|
// check. See the note in |
|
// sun.tools.javac.BatchEnvironment#setExemptPackages() |
|
|
|
if (env.isExemptPackage(nm)) { |
|
resolvedPackages.addElement(t); |
|
continue; |
|
} |
|
|
|
|
|
try { |
|
Identifier rnm = env.resolvePackageQualifiedName(nm); |
|
if (importable(rnm, env)) { |
|
|
|
if (env.getPackage(rnm.getTopName()).exists()) { |
|
env.error(where, "class.and.package", |
|
rnm.getTopName()); |
|
} |
|
|
|
if (!rnm.isInner()) |
|
rnm = Identifier.lookupInner(rnm, idNull); |
|
nm = rnm; |
|
} else if (!env.getPackage(nm).exists()) { |
|
env.error(where, "package.not.found", nm, "import"); |
|
} else if (rnm.isInner()) { |
|
|
|
env.error(where, "class.and.package", rnm.getTopName()); |
|
} |
|
resolvedPackages.addElement(new IdentifierToken(where, nm)); |
|
} catch (IOException ee) { |
|
env.error(where, "io.exception", "import"); |
|
} |
|
} |
|
packages = resolvedPackages; |
|
|
|
for (Enumeration e = singles.elements() ; e.hasMoreElements() ;) { |
|
IdentifierToken t = (IdentifierToken)e.nextElement(); |
|
Identifier nm = t.getName(); |
|
long where = t.getWhere(); |
|
Identifier pkg = nm.getQualifier(); |
|
|
|
|
|
nm = env.resolvePackageQualifiedName(nm); |
|
if (!env.classExists(nm.getTopName())) { |
|
env.error(where, "class.not.found", nm, "import"); |
|
} |
|
|
|
|
|
Identifier snm = nm.getFlatName().getName(); |
|
|
|
|
|
Identifier className = (Identifier)classes.get(snm); |
|
if (className != null) { |
|
Identifier f1 = Identifier.lookup(className.getQualifier(), |
|
className.getFlatName()); |
|
Identifier f2 = Identifier.lookup(nm.getQualifier(), |
|
nm.getFlatName()); |
|
if (!f1.equals(f2)) { |
|
env.error(where, "ambig.class", nm, className); |
|
} |
|
} |
|
classes.put(snm, nm); |
|
|
|
|
|
// The code here needs to check to see, if we |
|
// are importing an inner class, that all of its |
|
// enclosing classes are visible to us. To check this, |
|
// we need to construct a definition for the class. |
|
// The code here used to call... |
|
// |
|
// ClassDefinition def = env.getClassDefinition(nm); |
|
// |
|
// ...but that interfered with the basicCheck()'ing of |
|
// interfaces in certain cases (bug no. 4086139). Never |
|
// fear. Instead we load the class with a call to the |
|
// new getClassDefinitionNoCheck() which does no basicCheck() and |
|
// lets us answer the questions we are interested in w/o |
|
// interfering with the demand-driven nature of basicCheck(). |
|
|
|
try { |
|
|
|
ClassDeclaration decl = env.getClassDeclaration(nm); |
|
|
|
|
|
ClassDefinition def = decl.getClassDefinitionNoCheck(env); |
|
|
|
// Get the true name of the package containing this class. |
|
// `pkg' from above is insufficient. It includes the |
|
|
|
Identifier importedPackage = def.getName().getQualifier(); |
|
|
|
// Walk out the outerClass chain, ensuring that each level |
|
|
|
for (; def != null; def = def.getOuterClass()) { |
|
if (def.isPrivate() |
|
|| !(def.isPublic() |
|
|| importedPackage.equals(currentPackage))) { |
|
env.error(where, "cant.access.class", def); |
|
break; |
|
} |
|
} |
|
} catch (AmbiguousClass ee) { |
|
env.error(where, "ambig.class", ee.name1, ee.name2); |
|
} catch (ClassNotFound ee) { |
|
env.error(where, "class.not.found", ee.name, "import"); |
|
} |
|
} |
|
checked = 1; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized Identifier resolve(Environment env, Identifier nm) throws ClassNotFound { |
|
if (tracing) env.dtEnter("Imports.resolve: " + nm); |
|
|
|
// If the class has the special ambiguous prefix, then we will |
|
// get the original AmbiguousClass exception by removing the |
|
// prefix and proceeding in the normal fashion. |
|
|
|
if (nm.hasAmbigPrefix()) { |
|
nm = nm.removeAmbigPrefix(); |
|
} |
|
|
|
if (nm.isQualified()) { |
|
|
|
if (tracing) env.dtExit("Imports.resolve: QUALIFIED " + nm); |
|
return nm; |
|
} |
|
|
|
if (checked <= 0) { |
|
checked = 0; |
|
resolve(env); |
|
} |
|
|
|
|
|
Identifier className = (Identifier)classes.get(nm); |
|
if (className != null) { |
|
if (tracing) env.dtExit("Imports.resolve: PREVIOUSLY IMPORTED " + nm); |
|
return className; |
|
} |
|
|
|
// Note: the section below has changed a bit during the fix |
|
// for bug 4093217. The current package is no longer grouped |
|
// with the rest of the import-on-demands; it is now checked |
|
// separately. Also, the list of import-on-demands is now |
|
// guarranteed to be duplicate-free, so the code below can afford |
|
// to be a bit simpler. |
|
|
|
// First we look in the current package. The current package |
|
// is given precedence over the rest of the import-on-demands, |
|
// which means, among other things, that a class in the current |
|
|
|
Identifier id = Identifier.lookup(currentPackage, nm); |
|
if (importable(id, env)) { |
|
className = id; |
|
} else { |
|
// If it isn't in the current package, try to find it in |
|
|
|
Enumeration e = packages.elements(); |
|
while (e.hasMoreElements()) { |
|
IdentifierToken t = (IdentifierToken)e.nextElement(); |
|
id = Identifier.lookup(t.getName(), nm); |
|
|
|
if (importable(id, env)) { |
|
if (className == null) { |
|
// We haven't found any other matching classes yet. |
|
// Set className to what we've found and continue |
|
|
|
className = id; |
|
} else { |
|
if (tracing) |
|
env.dtExit("Imports.resolve: AMBIGUOUS " + nm); |
|
|
|
|
|
throw new AmbiguousClass(className, id); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
if (className == null) { |
|
if (tracing) env.dtExit("Imports.resolve: NOT FOUND " + nm); |
|
throw new ClassNotFound(nm); |
|
} |
|
|
|
|
|
classes.put(nm, className); |
|
if (tracing) env.dtExit("Imports.resolve: FIRST IMPORT " + nm); |
|
return className; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
static public boolean importable(Identifier id, Environment env) { |
|
if (!id.isInner()) { |
|
return env.classExists(id); |
|
} else if (!env.classExists(id.getTopName())) { |
|
return false; |
|
} else { |
|
|
|
try { |
|
// There used to be a call to... |
|
// env.getClassDeclaration(id.getTopName()); |
|
// ...here. It has been replaced with the |
|
// two statements below. These should be functionally |
|
// the same except for the fact that |
|
// getClassDefinitionNoCheck() does not call |
|
// basicCheck(). This allows us to avoid a circular |
|
// need to do basicChecking that can arise with |
|
// certain patterns of importing and inheritance. |
|
// This is a fix for a variant of bug 4086139. |
|
// |
|
// Note: the special case code in env.getClassDefinition() |
|
// which handles inner class names is not replicated below. |
|
// This should be okay, as we are looking up id.getTopName(), |
|
|
|
ClassDeclaration decl = |
|
env.getClassDeclaration(id.getTopName()); |
|
ClassDefinition c = |
|
decl.getClassDefinitionNoCheck(env); |
|
|
|
return c.innerClassExists(id.getFlatName().getTail()); |
|
} catch (ClassNotFound ee) { |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized Identifier forceResolve(Environment env, Identifier nm) { |
|
if (nm.isQualified()) |
|
return nm; |
|
|
|
Identifier className = (Identifier)classes.get(nm); |
|
if (className != null) { |
|
return className; |
|
} |
|
|
|
className = Identifier.lookup(currentPackage, nm); |
|
|
|
classes.put(nm, className); |
|
return className; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public synchronized void addClass(IdentifierToken t) { |
|
singles.addElement(t); |
|
} |
|
|
|
public void addClass(Identifier nm) throws AmbiguousClass { |
|
addClass(new IdentifierToken(nm)); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized void addPackage(IdentifierToken t) { |
|
final Identifier name = t.getName(); |
|
|
|
// If this is a duplicate import for the current package, |
|
|
|
if (name == currentPackage) { |
|
return; |
|
} |
|
|
|
// If this is a duplicate of a package which has already been |
|
|
|
final int size = packages.size(); |
|
for (int i = 0; i < size; i++) { |
|
if (name == ((IdentifierToken)packages.elementAt(i)).getName()) { |
|
return; |
|
} |
|
} |
|
|
|
|
|
packages.addElement(t); |
|
} |
|
|
|
public void addPackage(Identifier id) { |
|
addPackage(new IdentifierToken(id)); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public synchronized void setCurrentPackage(IdentifierToken t) { |
|
currentPackage = t.getName(); |
|
currentPackageWhere = t.getWhere(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public synchronized void setCurrentPackage(Identifier id) { |
|
currentPackage = id; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Identifier getCurrentPackage() { |
|
return currentPackage; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public List getImportedPackages() { |
|
return Collections.unmodifiableList(packages); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public List getImportedClasses() { |
|
return Collections.unmodifiableList(singles); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Environment newEnvironment(Environment env) { |
|
return new ImportEnvironment(env, this); |
|
} |
|
} |
|
|
|
final |
|
class ImportEnvironment extends Environment { |
|
Imports imports; |
|
|
|
ImportEnvironment(Environment env, Imports imports) { |
|
super(env, env.getSource()); |
|
this.imports = imports; |
|
} |
|
|
|
public Identifier resolve(Identifier nm) throws ClassNotFound { |
|
return imports.resolve(this, nm); |
|
} |
|
|
|
public Imports getImports() { |
|
return imports; |
|
} |
|
} |