| /* |
| * Copyright (c) 1994, 2003, 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.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 { |
| /** |
| * The current package, which is implicitly imported, |
| * and has precedence over other imported packages. |
| */ |
| Identifier currentPackage = idNull; |
| |
| /** |
| * A location for the current package declaration. Used to |
| * report errors against the current package. |
| */ |
| long currentPackageWhere = 0; |
| |
| /** |
| * The imported classes, including memoized imports from packages. |
| */ |
| Hashtable classes = new Hashtable(); |
| |
| /** |
| * The imported package identifiers. This will not contain duplicate |
| * imports for the same package. It will also not contain the |
| * current package. |
| */ |
| Vector packages = new Vector(); |
| |
| /** |
| * The (originally) imported classes. |
| * A vector of IdentifierToken. |
| */ |
| Vector singles = new Vector(); |
| |
| /** |
| * Are the import names checked yet? |
| */ |
| protected int checked; |
| |
| /** |
| * Constructor, always import java.lang. |
| */ |
| public Imports(Environment env) { |
| addPackage(idJavaLang); |
| } |
| |
| /** |
| * Check the names of the imports. |
| */ |
| 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() |
| // for more information. |
| if (env.isExemptPackage(nm)) { |
| resolvedPackages.addElement(t); |
| continue; |
| } |
| |
| // (Note: This code is moved from BatchParser.importPackage().) |
| try { |
| Identifier rnm = env.resolvePackageQualifiedName(nm); |
| if (importable(rnm, env)) { |
| // This name is a real class; better not be a package too. |
| if (env.getPackage(rnm.getTopName()).exists()) { |
| env.error(where, "class.and.package", |
| rnm.getTopName()); |
| } |
| // Pass an "inner" name to the imports. |
| 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()) { |
| // nm exists, and rnm.getTopName() is a parent package |
| 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(); |
| |
| // (Note: This code is moved from BatchParser.importClass().) |
| nm = env.resolvePackageQualifiedName(nm); |
| if (!env.classExists(nm.getTopName())) { |
| env.error(where, "class.not.found", nm, "import"); |
| } |
| |
| // (Note: This code is moved from Imports.addClass().) |
| Identifier snm = nm.getFlatName().getName(); |
| |
| // make sure it isn't already imported explicitly |
| 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 { |
| // Get a declaration |
| ClassDeclaration decl = env.getClassDeclaration(nm); |
| |
| // Get the definition (no env argument) |
| ClassDefinition def = decl.getClassDefinitionNoCheck(env); |
| |
| // Get the true name of the package containing this class. |
| // `pkg' from above is insufficient. It includes the |
| // names of our enclosing classes. Fix for 4086815. |
| Identifier importedPackage = def.getName().getQualifier(); |
| |
| // Walk out the outerClass chain, ensuring that each level |
| // is visible from our perspective. |
| 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; |
| } |
| |
| /** |
| * Lookup a class, given the current set of imports, |
| * AmbiguousClass exception is thrown if the name can be |
| * resolved in more than one way. A ClassNotFound exception |
| * is thrown if the class is not found in the imported classes |
| * and packages. |
| */ |
| 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. |
| // (part of solution for 4059855) |
| if (nm.hasAmbigPrefix()) { |
| nm = nm.removeAmbigPrefix(); |
| } |
| |
| if (nm.isQualified()) { |
| // Don't bother it is already qualified |
| if (tracing) env.dtExit("Imports.resolve: QUALIFIED " + nm); |
| return nm; |
| } |
| |
| if (checked <= 0) { |
| checked = 0; |
| resolve(env); |
| } |
| |
| // Check if it was imported before |
| 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 |
| // package cannot be ambiguous. |
| 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 |
| // our import-on-demands. |
| 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 |
| // looking for an ambiguity. |
| className = id; |
| } else { |
| if (tracing) |
| env.dtExit("Imports.resolve: AMBIGUOUS " + nm); |
| |
| // We've found an ambiguity. |
| throw new AmbiguousClass(className, id); |
| } |
| } |
| } |
| } |
| |
| // Make sure a class was found |
| if (className == null) { |
| if (tracing) env.dtExit("Imports.resolve: NOT FOUND " + nm); |
| throw new ClassNotFound(nm); |
| } |
| |
| // Remember the binding |
| classes.put(nm, className); |
| if (tracing) env.dtExit("Imports.resolve: FIRST IMPORT " + nm); |
| return className; |
| } |
| |
| /** |
| * Check to see if 'id' names an importable class in `env'. |
| * This method was made public and static for utility. |
| */ |
| static public boolean importable(Identifier id, Environment env) { |
| if (!id.isInner()) { |
| return env.classExists(id); |
| } else if (!env.classExists(id.getTopName())) { |
| return false; |
| } else { |
| // load the top class and look inside it |
| 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(), |
| // not id. |
| ClassDeclaration decl = |
| env.getClassDeclaration(id.getTopName()); |
| ClassDefinition c = |
| decl.getClassDefinitionNoCheck(env); |
| |
| return c.innerClassExists(id.getFlatName().getTail()); |
| } catch (ClassNotFound ee) { |
| return false; |
| } |
| } |
| } |
| |
| /** |
| * Suppose a resolve() call has failed. |
| * This routine can be used silently to give a reasonable |
| * default qualification (the current package) to the identifier. |
| * This decision is recorded for future reference. |
| */ |
| 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; |
| } |
| |
| /** |
| * Add a class import |
| */ |
| public synchronized void addClass(IdentifierToken t) { |
| singles.addElement(t); |
| } |
| // for compatibility |
| public void addClass(Identifier nm) throws AmbiguousClass { |
| addClass(new IdentifierToken(nm)); |
| } |
| |
| /** |
| * Add a package import, or perhaps an inner class scope. |
| * Ignore any duplicate imports. |
| */ |
| public synchronized void addPackage(IdentifierToken t) { |
| final Identifier name = t.getName(); |
| |
| // If this is a duplicate import for the current package, |
| // ignore it. |
| if (name == currentPackage) { |
| return; |
| } |
| |
| // If this is a duplicate of a package which has already been |
| // added to the list, ignore it. |
| final int size = packages.size(); |
| for (int i = 0; i < size; i++) { |
| if (name == ((IdentifierToken)packages.elementAt(i)).getName()) { |
| return; |
| } |
| } |
| |
| // Add the package to the list. |
| packages.addElement(t); |
| } |
| // for compatibility |
| public void addPackage(Identifier id) { |
| addPackage(new IdentifierToken(id)); |
| } |
| |
| /** |
| * Specify the current package with an IdentifierToken. |
| */ |
| public synchronized void setCurrentPackage(IdentifierToken t) { |
| currentPackage = t.getName(); |
| currentPackageWhere = t.getWhere(); |
| } |
| |
| /** |
| * Specify the current package |
| */ |
| public synchronized void setCurrentPackage(Identifier id) { |
| currentPackage = id; |
| } |
| |
| /** |
| * Report the current package |
| */ |
| public Identifier getCurrentPackage() { |
| return currentPackage; |
| } |
| |
| /** |
| * Return an unmodifiable list of IdentifierToken representing |
| * packages specified as imports. |
| */ |
| public List getImportedPackages() { |
| return Collections.unmodifiableList(packages); |
| } |
| |
| /** |
| * Return an unmodifiable list of IdentifierToken representing |
| * classes specified as imports. |
| */ |
| public List getImportedClasses() { |
| return Collections.unmodifiableList(singles); |
| } |
| |
| /** |
| * Extend an environment with my resolve() method. |
| */ |
| 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; |
| } |
| } |