View Javadoc
1   /*
2    * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.tools.java;
27  
28  import java.util.Hashtable;
29  import java.util.Vector;
30  import java.util.Enumeration;
31  import java.util.List;
32  import java.util.Collections;
33  import java.io.IOException;
34  
35  /**
36   * This class describes the classes and packages imported
37   * from a source file. A Hashtable called bindings is maintained
38   * to quickly map symbol names to classes. This table is flushed
39   * everytime a new import is added.
40   *
41   * A class name is resolved as follows:
42   *  - if it is a qualified name then return the corresponding class
43   *  - if the name corresponds to an individually imported class then return that class
44   *  - check if the class is defined in any of the imported packages,
45   *    if it is then return it, make sure it is defined in only one package
46   *  - assume that the class is defined in the current package
47   *
48   * WARNING: The contents of this source file are not part of any
49   * supported API.  Code that depends on them does so at its own risk:
50   * they are subject to change or removal without notice.
51   */
52  
53  public
54  class Imports implements Constants {
55      /**
56       * The current package, which is implicitly imported,
57       * and has precedence over other imported packages.
58       */
59      Identifier currentPackage = idNull;
60  
61      /**
62       * A location for the current package declaration.  Used to
63       * report errors against the current package.
64       */
65      long currentPackageWhere = 0;
66  
67      /**
68       * The imported classes, including memoized imports from packages.
69       */
70      Hashtable classes = new Hashtable();
71  
72      /**
73       * The imported package identifiers.  This will not contain duplicate
74       * imports for the same package.  It will also not contain the
75       * current package.
76       */
77      Vector packages = new Vector();
78  
79      /**
80       * The (originally) imported classes.
81       * A vector of IdentifierToken.
82       */
83      Vector singles = new Vector();
84  
85      /**
86       * Are the import names checked yet?
87       */
88      protected int checked;
89  
90      /**
91       * Constructor, always import java.lang.
92       */
93      public Imports(Environment env) {
94          addPackage(idJavaLang);
95      }
96  
97      /**
98       * Check the names of the imports.
99       */
100     public synchronized void resolve(Environment env) {
101         if (checked != 0) {
102             return;
103         }
104         checked = -1;
105 
106         // After all class information has been read, now we can
107         // safely inspect import information for errors.
108         // If we did this before all parsing was finished,
109         // we could get vicious circularities, since files can
110         // import each others' classes.
111 
112         // A note: the resolution of the package java.lang takes place
113         // in the sun.tools.javac.BatchEnvironment#setExemptPackages().
114 
115         // Make sure that the current package's name does not collide
116         // with the name of an existing class. (bug 4101529)
117         //
118         // This change has been backed out because, on WIN32, it
119         // failed to distinguish between java.awt.event and
120         // java.awt.Event when looking for a directory.  We will
121         // add this back in later.
122         //
123         // if (currentPackage != idNull) {
124         //    Identifier resolvedName =
125         //      env.resolvePackageQualifiedName(currentPackage);
126         //
127         //   Identifier className = resolvedName.getTopName();
128         //
129         //   if (importable(className, env)) {
130         //      // The name of the current package is also the name
131         //      // of a class.
132         //      env.error(currentPackageWhere, "package.class.conflict",
133         //                currentPackage, className);
134         //     }
135         // }
136 
137         Vector resolvedPackages = new Vector();
138         for (Enumeration e = packages.elements() ; e.hasMoreElements() ;) {
139             IdentifierToken t = (IdentifierToken)e.nextElement();
140             Identifier nm = t.getName();
141             long where = t.getWhere();
142 
143             // Check to see if this package is exempt from the "exists"
144             // check.  See the note in
145             // sun.tools.javac.BatchEnvironment#setExemptPackages()
146             // for more information.
147             if (env.isExemptPackage(nm)) {
148                 resolvedPackages.addElement(t);
149                 continue;
150             }
151 
152             // (Note: This code is moved from BatchParser.importPackage().)
153             try {
154                 Identifier rnm = env.resolvePackageQualifiedName(nm);
155                 if (importable(rnm, env)) {
156                     // This name is a real class; better not be a package too.
157                     if (env.getPackage(rnm.getTopName()).exists()) {
158                         env.error(where, "class.and.package",
159                                   rnm.getTopName());
160                     }
161                     // Pass an "inner" name to the imports.
162                     if (!rnm.isInner())
163                         rnm = Identifier.lookupInner(rnm, idNull);
164                     nm = rnm;
165                 } else if (!env.getPackage(nm).exists()) {
166                     env.error(where, "package.not.found", nm, "import");
167                 } else if (rnm.isInner()) {
168                     // nm exists, and rnm.getTopName() is a parent package
169                     env.error(where, "class.and.package", rnm.getTopName());
170                 }
171                 resolvedPackages.addElement(new IdentifierToken(where, nm));
172             } catch (IOException ee) {
173                 env.error(where, "io.exception", "import");
174             }
175         }
176         packages = resolvedPackages;
177 
178         for (Enumeration e = singles.elements() ; e.hasMoreElements() ;) {
179             IdentifierToken t = (IdentifierToken)e.nextElement();
180             Identifier nm = t.getName();
181             long where = t.getWhere();
182             Identifier pkg = nm.getQualifier();
183 
184             // (Note: This code is moved from BatchParser.importClass().)
185             nm = env.resolvePackageQualifiedName(nm);
186             if (!env.classExists(nm.getTopName())) {
187                 env.error(where, "class.not.found", nm, "import");
188             }
189 
190             // (Note: This code is moved from Imports.addClass().)
191             Identifier snm = nm.getFlatName().getName();
192 
193             // make sure it isn't already imported explicitly
194             Identifier className = (Identifier)classes.get(snm);
195             if (className != null) {
196                 Identifier f1 = Identifier.lookup(className.getQualifier(),
197                                                   className.getFlatName());
198                 Identifier f2 = Identifier.lookup(nm.getQualifier(),
199                                                   nm.getFlatName());
200                 if (!f1.equals(f2)) {
201                     env.error(where, "ambig.class", nm, className);
202                 }
203             }
204             classes.put(snm, nm);
205 
206 
207             // The code here needs to check to see, if we
208             // are importing an inner class, that all of its
209             // enclosing classes are visible to us.  To check this,
210             // we need to construct a definition for the class.
211             // The code here used to call...
212             //
213             //     ClassDefinition def = env.getClassDefinition(nm);
214             //
215             // ...but that interfered with the basicCheck()'ing of
216             // interfaces in certain cases (bug no. 4086139).  Never
217             // fear.  Instead we load the class with a call to the
218             // new getClassDefinitionNoCheck() which does no basicCheck() and
219             // lets us answer the questions we are interested in w/o
220             // interfering with the demand-driven nature of basicCheck().
221 
222             try {
223                 // Get a declaration
224                 ClassDeclaration decl = env.getClassDeclaration(nm);
225 
226                 // Get the definition (no env argument)
227                 ClassDefinition def = decl.getClassDefinitionNoCheck(env);
228 
229                 // Get the true name of the package containing this class.
230                 // `pkg' from above is insufficient.  It includes the
231                 // names of our enclosing classes.  Fix for 4086815.
232                 Identifier importedPackage = def.getName().getQualifier();
233 
234                 // Walk out the outerClass chain, ensuring that each level
235                 // is visible from our perspective.
236                 for (; def != null; def = def.getOuterClass()) {
237                     if (def.isPrivate()
238                         || !(def.isPublic()
239                              || importedPackage.equals(currentPackage))) {
240                         env.error(where, "cant.access.class", def);
241                         break;
242                     }
243                 }
244             } catch (AmbiguousClass ee) {
245                 env.error(where, "ambig.class", ee.name1, ee.name2);
246             } catch (ClassNotFound ee) {
247                 env.error(where, "class.not.found", ee.name, "import");
248             }
249         }
250         checked = 1;
251     }
252 
253     /**
254      * Lookup a class, given the current set of imports,
255      * AmbiguousClass exception is thrown if the name can be
256      * resolved in more than one way. A ClassNotFound exception
257      * is thrown if the class is not found in the imported classes
258      * and packages.
259      */
260     public synchronized Identifier resolve(Environment env, Identifier nm) throws ClassNotFound {
261         if (tracing) env.dtEnter("Imports.resolve: " + nm);
262 
263         // If the class has the special ambiguous prefix, then we will
264         // get the original AmbiguousClass exception by removing the
265         // prefix and proceeding in the normal fashion.
266         // (part of solution for 4059855)
267         if (nm.hasAmbigPrefix()) {
268             nm = nm.removeAmbigPrefix();
269         }
270 
271         if (nm.isQualified()) {
272             // Don't bother it is already qualified
273             if (tracing) env.dtExit("Imports.resolve: QUALIFIED " + nm);
274             return nm;
275         }
276 
277         if (checked <= 0) {
278             checked = 0;
279             resolve(env);
280         }
281 
282         // Check if it was imported before
283         Identifier className = (Identifier)classes.get(nm);
284         if (className != null) {
285             if (tracing) env.dtExit("Imports.resolve: PREVIOUSLY IMPORTED " + nm);
286             return className;
287         }
288 
289         // Note: the section below has changed a bit during the fix
290         // for bug 4093217.  The current package is no longer grouped
291         // with the rest of the import-on-demands; it is now checked
292         // separately.  Also, the list of import-on-demands is now
293         // guarranteed to be duplicate-free, so the code below can afford
294         // to be a bit simpler.
295 
296         // First we look in the current package.  The current package
297         // is given precedence over the rest of the import-on-demands,
298         // which means, among other things, that a class in the current
299         // package cannot be ambiguous.
300         Identifier id = Identifier.lookup(currentPackage, nm);
301         if (importable(id, env)) {
302             className = id;
303         } else {
304             // If it isn't in the current package, try to find it in
305             // our import-on-demands.
306             Enumeration e = packages.elements();
307             while (e.hasMoreElements()) {
308                 IdentifierToken t = (IdentifierToken)e.nextElement();
309                 id = Identifier.lookup(t.getName(), nm);
310 
311                 if (importable(id, env)) {
312                     if (className == null) {
313                         // We haven't found any other matching classes yet.
314                         // Set className to what we've found and continue
315                         // looking for an ambiguity.
316                         className = id;
317                     } else {
318                         if (tracing)
319                             env.dtExit("Imports.resolve: AMBIGUOUS " + nm);
320 
321                         // We've found an ambiguity.
322                         throw new AmbiguousClass(className, id);
323                     }
324                 }
325             }
326         }
327 
328         // Make sure a class was found
329         if (className == null) {
330             if (tracing) env.dtExit("Imports.resolve: NOT FOUND " + nm);
331             throw new ClassNotFound(nm);
332         }
333 
334         // Remember the binding
335         classes.put(nm, className);
336         if (tracing) env.dtExit("Imports.resolve: FIRST IMPORT " + nm);
337         return className;
338     }
339 
340     /**
341      * Check to see if 'id' names an importable class in `env'.
342      * This method was made public and static for utility.
343      */
344     static public boolean importable(Identifier id, Environment env) {
345         if (!id.isInner()) {
346             return env.classExists(id);
347         } else if (!env.classExists(id.getTopName())) {
348             return false;
349         } else {
350             // load the top class and look inside it
351             try {
352                 // There used to be a call to...
353                 //    env.getClassDeclaration(id.getTopName());
354                 // ...here.  It has been replaced with the
355                 // two statements below.  These should be functionally
356                 // the same except for the fact that
357                 // getClassDefinitionNoCheck() does not call
358                 // basicCheck().  This allows us to avoid a circular
359                 // need to do basicChecking that can arise with
360                 // certain patterns of importing and inheritance.
361                 // This is a fix for a variant of bug 4086139.
362                 //
363                 // Note: the special case code in env.getClassDefinition()
364                 // which handles inner class names is not replicated below.
365                 // This should be okay, as we are looking up id.getTopName(),
366                 // not id.
367                 ClassDeclaration decl =
368                     env.getClassDeclaration(id.getTopName());
369                 ClassDefinition c =
370                     decl.getClassDefinitionNoCheck(env);
371 
372                 return c.innerClassExists(id.getFlatName().getTail());
373             } catch (ClassNotFound ee) {
374                 return false;
375             }
376         }
377     }
378 
379     /**
380      * Suppose a resolve() call has failed.
381      * This routine can be used silently to give a reasonable
382      * default qualification (the current package) to the identifier.
383      * This decision is recorded for future reference.
384      */
385     public synchronized Identifier forceResolve(Environment env, Identifier nm) {
386         if (nm.isQualified())
387             return nm;
388 
389         Identifier className = (Identifier)classes.get(nm);
390         if (className != null) {
391             return className;
392         }
393 
394         className = Identifier.lookup(currentPackage, nm);
395 
396         classes.put(nm, className);
397         return className;
398     }
399 
400     /**
401      * Add a class import
402      */
403     public synchronized void addClass(IdentifierToken t) {
404         singles.addElement(t);
405     }
406     // for compatibility
407     public void addClass(Identifier nm) throws AmbiguousClass {
408         addClass(new IdentifierToken(nm));
409     }
410 
411     /**
412      * Add a package import, or perhaps an inner class scope.
413      * Ignore any duplicate imports.
414      */
415     public synchronized void addPackage(IdentifierToken t) {
416         final Identifier name = t.getName();
417 
418         // If this is a duplicate import for the current package,
419         // ignore it.
420         if (name == currentPackage) {
421             return;
422         }
423 
424         // If this is a duplicate of a package which has already been
425         // added to the list, ignore it.
426         final int size = packages.size();
427         for (int i = 0; i < size; i++) {
428             if (name == ((IdentifierToken)packages.elementAt(i)).getName()) {
429                 return;
430             }
431         }
432 
433         // Add the package to the list.
434         packages.addElement(t);
435     }
436     // for compatibility
437     public void addPackage(Identifier id) {
438         addPackage(new IdentifierToken(id));
439     }
440 
441     /**
442      * Specify the current package with an IdentifierToken.
443      */
444     public synchronized void setCurrentPackage(IdentifierToken t) {
445         currentPackage = t.getName();
446         currentPackageWhere = t.getWhere();
447     }
448 
449     /**
450      * Specify the current package
451      */
452     public synchronized void setCurrentPackage(Identifier id) {
453         currentPackage = id;
454     }
455 
456     /**
457      * Report the current package
458      */
459     public Identifier getCurrentPackage() {
460         return currentPackage;
461     }
462 
463     /**
464      * Return an unmodifiable list of IdentifierToken representing
465      * packages specified as imports.
466      */
467     public List getImportedPackages() {
468         return Collections.unmodifiableList(packages);
469     }
470 
471     /**
472      * Return an unmodifiable list of IdentifierToken representing
473      * classes specified as imports.
474      */
475     public List getImportedClasses() {
476         return Collections.unmodifiableList(singles);
477     }
478 
479     /**
480      * Extend an environment with my resolve() method.
481      */
482     public Environment newEnvironment(Environment env) {
483         return new ImportEnvironment(env, this);
484     }
485 }
486 
487 final
488 class ImportEnvironment extends Environment {
489     Imports imports;
490 
491     ImportEnvironment(Environment env, Imports imports) {
492         super(env, env.getSource());
493         this.imports = imports;
494     }
495 
496     public Identifier resolve(Identifier nm) throws ClassNotFound {
497         return imports.resolve(this, nm);
498     }
499 
500     public Imports getImports() {
501         return imports;
502     }
503 }