1/*
2 * Copyright (c) 1994, 2004, 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
26package sun.tools.javac;
27
28import sun.tools.java.*;
29import sun.tools.tree.Node;
30import sun.tools.java.Package;
31
32import java.util.*;
33import java.io.*;
34
35/**
36 * Main environment of the batch version of the Java compiler,
37 * this needs more work.
38 *
39 * WARNING: The contents of this source file are not part of any
40 * supported API.  Code that depends on them does so at its own risk:
41 * they are subject to change or removal without notice.
42 */
43@Deprecated
44public
45class BatchEnvironment extends Environment implements ErrorConsumer {
46    /**
47     * The stream where error message are printed.
48     */
49    OutputStream out;
50
51    /**
52     * The path we use for finding source files.
53     */
54    protected ClassPath sourcePath;
55
56    /**
57     * The path we use for finding class (binary) files.
58     */
59    protected ClassPath binaryPath;
60
61    /**
62     * A hashtable of resource contexts.
63     */
64    Hashtable<Identifier, Package> packages = new Hashtable<>(31);
65
66    /**
67     * The classes, in order of appearance.
68     */
69    Vector<ClassDeclaration> classesOrdered = new Vector<>();
70
71    /**
72     * The classes, keyed by ClassDeclaration.
73     */
74    Hashtable<Type, ClassDeclaration> classes = new Hashtable<>(351);
75
76    /**
77     * flags
78     */
79    public int flags;
80
81    /**
82     * Major and minor versions to use for generated class files.
83     * Environments that extend BatchEnvironment (such as javadoc's
84     * Env class) get the default values below.
85     *
86     * javac itself may override these versions with values determined
87     * from the command line "-target" option.
88     */
89    public short majorVersion = JAVA_DEFAULT_VERSION;
90    public short minorVersion = JAVA_DEFAULT_MINOR_VERSION;
91
92// JCOV
93    /**
94     * coverage data file
95     */
96    public File covFile;
97// end JCOV
98
99    /**
100     * The number of errors and warnings
101     */
102    public int nerrors;
103    public int nwarnings;
104    public int ndeprecations;
105
106    /**
107     * A list of files containing deprecation warnings.
108     */
109    Vector<Object> deprecationFiles = new Vector<>();
110
111        /**
112         * writes out error messages
113         */
114
115        ErrorConsumer errorConsumer;
116
117    /**
118     * Old constructors -- these constructors build a BatchEnvironment
119     * with an old-style class path.
120     */
121    public BatchEnvironment(ClassPath path) {
122        this(System.out, path);
123    }
124    public BatchEnvironment(OutputStream out,
125                            ClassPath path) {
126        this(out, path, (ErrorConsumer) null);
127    }
128    public BatchEnvironment(OutputStream out,
129                            ClassPath path,
130                            ErrorConsumer errorConsumer) {
131        this(out, path, path, errorConsumer);
132    }
133
134    /**
135     * New constructors -- these constructors build a BatchEnvironment
136     * with a source path and a binary path.
137     */
138    public BatchEnvironment(ClassPath sourcePath,
139                            ClassPath binaryPath) {
140        this(System.out, sourcePath, binaryPath);
141    }
142    public BatchEnvironment(OutputStream out,
143                            ClassPath sourcePath,
144                            ClassPath binaryPath) {
145        this(out, sourcePath, binaryPath, (ErrorConsumer) null);
146    }
147    public BatchEnvironment(OutputStream out,
148                            ClassPath sourcePath,
149                            ClassPath binaryPath,
150                            ErrorConsumer errorConsumer) {
151        this.out = out;
152        this.sourcePath = sourcePath;
153        this.binaryPath = binaryPath;
154        this.errorConsumer = (errorConsumer == null) ? this : errorConsumer;
155    }
156
157    /**
158     * Factory
159     */
160    static BatchEnvironment create(OutputStream out,
161                                   String srcPathString,
162                                   String classPathString,
163                                   String sysClassPathString) {
164        ClassPath[] classPaths = classPaths(srcPathString, classPathString,
165                                            sysClassPathString);
166        return new BatchEnvironment(out, classPaths[0], classPaths[1]);
167    }
168
169    protected static ClassPath[] classPaths(String srcPathString,
170                                            String classPathString,
171                                            String sysClassPathString) {
172        // Create our source classpath and our binary classpath
173        ClassPath sourcePath;
174        ClassPath binaryPath;
175        StringBuffer binaryPathBuffer = new StringBuffer();
176
177        if (classPathString == null) {
178            // The env.class.path property is the user's CLASSPATH
179            // environment variable, and it set by the wrapper (ie,
180            // javac.exe).
181            classPathString = System.getProperty("env.class.path");
182            if (classPathString == null) {
183                classPathString = ".";
184            }
185        }
186        if (srcPathString == null) {
187            srcPathString = classPathString;
188        }
189        if (sysClassPathString == null) {
190            sysClassPathString = System.getProperty("sun.boot.class.path");
191            if (sysClassPathString == null) { // shouldn't happen; recover gracefully
192                sysClassPathString = classPathString;
193            }
194        }
195        appendPath(binaryPathBuffer, sysClassPathString);
196
197        appendPath(binaryPathBuffer, classPathString);
198
199        sourcePath = new ClassPath(srcPathString);
200        binaryPath = new ClassPath(binaryPathBuffer.toString());
201
202        return new ClassPath[]{sourcePath, binaryPath};
203    }
204
205    private static void appendPath(StringBuffer buf, String str) {
206        if (str.length() > 0) {
207            if (buf.length() > 0) {
208                buf.append(File.pathSeparator);
209            }
210            buf.append(str);
211        }
212    }
213
214    /**
215     * Return flags
216     */
217    public int getFlags() {
218        return flags;
219    }
220
221    /**
222     * Return major version to use for generated class files
223     */
224    public short getMajorVersion() {
225        return majorVersion;
226    }
227
228    /**
229     * Return minor version to use for generated class files
230     */
231    public short getMinorVersion() {
232        return minorVersion;
233    }
234
235// JCOV
236    /**
237     * Return coverage data file
238     */
239    public File getcovFile() {
240        return covFile;
241    }
242// end JCOV
243
244    /**
245     * Return an enumeration of all the currently defined classes
246     * in order of appearance to getClassDeclaration().
247     */
248    public Enumeration<ClassDeclaration> getClasses() {
249        return classesOrdered.elements();
250    }
251
252    /**
253     * A set of Identifiers for all packages exempt from the "exists"
254     * check in Imports#resolve().  These are the current packages for
255     * all classes being compiled as of the first call to isExemptPackage.
256     */
257    private Set<Identifier> exemptPackages;
258
259    /**
260     * Tells whether an Identifier refers to a package which should be
261     * exempt from the "exists" check in Imports#resolve().
262     */
263    public boolean isExemptPackage(Identifier id) {
264        if (exemptPackages == null) {
265            // Collect a list of the packages of all classes currently
266            // being compiled.
267            setExemptPackages();
268        }
269
270        return exemptPackages.contains(id);
271    }
272
273    /**
274     * Set the set of packages which are exempt from the exists check
275     * in Imports#resolve().
276     */
277    private void setExemptPackages() {
278        // The JLS gives us the freedom to define "accessibility" of
279        // a package in whatever manner we wish.  After the evaluation
280        // of bug 4093217, we have decided to consider a package P
281        // accessible if either:
282        //
283        // 1. The directory corresponding to P exists on the classpath.
284        // 2. For any class C currently being compiled, C belongs to
285        //    package P.
286        // 3. For any class C currently being compiled, C belongs to
287        //    package Q and Q is a subpackage of P.
288        //
289        // In order to implement this, we collect the current packages
290        // (and prefixes) of all packages we have found so far.  These
291        // will be exempt from the "exists" check in
292        // sun.tools.java.Imports#resolve().
293
294        exemptPackages = new HashSet<>(101);
295
296        // Add all of the current packages and their prefixes to our set.
297        for (Enumeration<ClassDeclaration> e = getClasses(); e.hasMoreElements(); ) {
298            ClassDeclaration c = e.nextElement();
299            if (c.getStatus() == CS_PARSED) {
300                SourceClass def = (SourceClass) c.getClassDefinition();
301                if (def.isLocal())
302                    continue;
303
304                Identifier pkg = def.getImports().getCurrentPackage();
305
306                // Add the name of this package and all of its prefixes
307                // to our set.
308                while (pkg != idNull && exemptPackages.add(pkg)) {
309                    pkg = pkg.getQualifier();
310                }
311            }
312        }
313
314        // Before we go any further, we make sure java.lang is
315        // accessible and that it is not ambiguous.  These checks
316        // are performed for "ordinary" packages in
317        // sun.tools.java.Imports#resolve().  The reason we perform
318        // them specially for java.lang is that we want to report
319        // the error once, and outside of any particular file.
320
321        // Check to see if java.lang is accessible.
322        if (!exemptPackages.contains(idJavaLang)) {
323            // Add java.lang to the set of exempt packages.
324            exemptPackages.add(idJavaLang);
325
326            try {
327                if (!getPackage(idJavaLang).exists()) {
328                    // java.lang doesn't exist.
329                    error(0, "package.not.found.strong", idJavaLang);
330                    return;
331                }
332            } catch (IOException ee) {
333                // We got an IO exception checking to see if the package
334                // java.lang exists.
335                error(0, "io.exception.package", idJavaLang);
336            }
337        }
338
339        // Next we ensure that java.lang is not both a class and
340        // a package.  (Fix for 4101529)
341        //
342        // This change has been backed out because, on WIN32, it
343        // failed to take character case into account.  It will
344        // be put back in later.
345        //
346        // Identifier resolvedName =
347        //   resolvePackageQualifiedName(idJavaLang);
348        // Identifier topClassName = resolvedName.getTopName();
349        //     //if (Imports.importable(topClassName, env)) {
350        // if (Imports.importable(topClassName, this)) {
351        //    // It is a package and a class.  Emit the error.
352        //    error(0, "package.class.conflict.strong",
353        //            idJavaLang, topClassName);
354        //    return;
355        // }
356    }
357
358    /**
359     * Get a class, given the fully qualified class name
360     */
361    public ClassDeclaration getClassDeclaration(Identifier nm) {
362        return getClassDeclaration(Type.tClass(nm));
363    }
364
365    public ClassDeclaration getClassDeclaration(Type t) {
366        ClassDeclaration c = classes.get(t);
367        if (c == null) {
368            classes.put(t, c = new ClassDeclaration(t.getClassName()));
369            classesOrdered.addElement(c);
370        }
371        return c;
372    }
373
374    /**
375     * Check if a class exists
376     * Applies only to package members (non-nested classes).
377     */
378    public boolean classExists(Identifier nm) {
379        if (nm.isInner()) {
380            nm = nm.getTopName();       // just in case
381        }
382        Type t = Type.tClass(nm);
383        try {
384            ClassDeclaration c = classes.get(t);
385            return (c != null) ? c.getName().equals(nm) :
386                getPackage(nm.getQualifier()).classExists(nm.getName());
387        } catch (IOException e) {
388            return true;
389        }
390    }
391
392    /**
393     * Generate a new name similar to the given one.
394     * Do it in such a way that repeated compilations of
395     * the same source generate the same series of names.
396     */
397
398    // This code does not perform as stated above.
399    // Correction below is part of fix for bug id 4056065.
400    //
401    // NOTE: The method 'generateName' has now been folded into its
402    // single caller, 'makeClassDefinition', which appears later in
403    // this file.
404
405    /*--------------------------*
406    public Identifier generateName(ClassDefinition outerClass, Identifier nm) {
407        Identifier outerNm = outerClass.getName();
408        Identifier flat = outerNm.getFlatName();
409        Identifier stem = Identifier.lookup(outerNm.getQualifier(),
410                                            flat.getHead());
411        for (int i = 1; ; i++) {
412            String name = i + (nm.equals(idNull) ? "" : SIG_INNERCLASS + nm);
413            Identifier nm1 = Identifier.lookupInner(stem,
414                                                    Identifier.lookup(name));
415            if (classes.get(Type.tClass(nm1)) == null)
416                return nm1;
417        }
418    }
419    *--------------------------*/
420
421    /**
422     * Get the package path for a package
423     */
424    public Package getPackage(Identifier pkg) throws IOException {
425        Package p = packages.get(pkg);
426        if (p == null) {
427            packages.put(pkg, p = new Package(sourcePath, binaryPath, pkg));
428        }
429        return p;
430    }
431
432    /**
433     * Parse a source file
434     */
435    public void parseFile(ClassFile file) throws FileNotFoundException {
436        long tm = System.currentTimeMillis();
437        InputStream input;
438        BatchParser p;
439
440        if (tracing) dtEnter("parseFile: PARSING SOURCE " + file);
441
442        Environment env = new Environment(this, file);
443
444        try {
445            input = file.getInputStream();
446            env.setCharacterEncoding(getCharacterEncoding());
447            //      p = new BatchParser(e, new BufferedInputStream(input));
448            p = new BatchParser(env, input);
449        } catch(IOException ex) {
450            if (tracing) dtEvent("parseFile: IO EXCEPTION " + file);
451            throw new FileNotFoundException();
452        }
453
454        try {
455            p.parseFile();
456        } catch(Exception e) {
457            throw new CompilerError(e);
458        }
459
460        try {
461            input.close();
462        } catch (IOException ex) {
463            // We're turn with the input, so ignore this.
464        }
465
466        if (verbose()) {
467            tm = System.currentTimeMillis() - tm;
468            output(Main.getText("benv.parsed_in", file.getPath(),
469                                Long.toString(tm)));
470        }
471
472        if (p.classes.size() == 0) {
473            // The JLS allows a file to contain no compilation units --
474            // that is, it allows a file to contain no classes or interfaces.
475            // In this case, we are still responsible for checking that the
476            // imports resolve properly.  The way the compiler is organized,
477            // this is the last point at which we still have enough information
478            // to do so. (Fix for 4041851).
479            p.imports.resolve(env);
480        } else {
481            // In an attempt to see that classes which come from the
482            // same source file are all recompiled when any one of them
483            // would be recompiled (when using the -depend option) we
484            // introduce artificial dependencies between these classes.
485            // We do this by calling the addDependency() method, which
486            // adds a (potentially unused) class reference to the constant
487            // pool of the class.
488            //
489            // Previously, we added a dependency from every class in the
490            // file, to every class in the file.  This introduced, in
491            // total, a quadratic number of potentially bogus constant
492            // pool entries.  This was bad.  Now we add our artificial
493            // dependencies in such a way that the classes are connected
494            // in a circle.  While single links is probably sufficient, the
495            // code below adds double links just to be diligent.
496            // (Fix for 4108286).
497            //
498            // Note that we don't chain in inner classes.  The links
499            // between them and their outerclass should be sufficient
500            // here.
501            // (Fix for 4107960).
502            //
503            // The dependency code was previously in BatchParser.java.
504            Enumeration<SourceClass> e = p.classes.elements();
505
506            // first will not be an inner class.
507            ClassDefinition first = e.nextElement();
508            if (first.isInnerClass()) {
509                throw new CompilerError("BatchEnvironment, first is inner");
510            }
511
512            ClassDefinition current = first;
513            ClassDefinition next;
514            while (e.hasMoreElements()) {
515                next = e.nextElement();
516                // Don't chain in inner classes.
517                if (next.isInnerClass()) {
518                    continue;
519                }
520                current.addDependency(next.getClassDeclaration());
521                next.addDependency(current.getClassDeclaration());
522                current = next;
523            }
524            // Make a circle.  Don't bother to add a dependency if there
525            // is only one class in the file.
526            if (current != first) {
527                current.addDependency(first.getClassDeclaration());
528                first.addDependency(current.getClassDeclaration());
529            }
530        }
531
532        if (tracing) dtExit("parseFile: SOURCE PARSED " + file);
533    }
534
535    /**
536     * Load a binary file
537     */
538    BinaryClass loadFile(ClassFile file) throws IOException {
539        long tm = System.currentTimeMillis();
540        InputStream input = file.getInputStream();
541        BinaryClass c = null;
542
543        if (tracing) dtEnter("loadFile: LOADING CLASSFILE " + file);
544
545        try {
546            DataInputStream is =
547                new DataInputStream(new BufferedInputStream(input));
548            c = BinaryClass.load(new Environment(this, file), is,
549                                 loadFileFlags());
550        } catch (ClassFormatError e) {
551            error(0, "class.format", file.getPath(), e.getMessage());
552            if (tracing) dtExit("loadFile: CLASS FORMAT ERROR " + file);
553            return null;
554        } catch (java.io.EOFException e) {
555            // If we get an EOF while processing a class file, then
556            // it has been truncated.  We let other I/O errors pass
557            // through.  Fix for 4088443.
558            error(0, "truncated.class", file.getPath());
559            return null;
560        }
561
562        input.close();
563        if (verbose()) {
564            tm = System.currentTimeMillis() - tm;
565            output(Main.getText("benv.loaded_in", file.getPath(),
566                                Long.toString(tm)));
567        }
568
569        if (tracing) dtExit("loadFile: CLASSFILE LOADED " + file);
570
571        return c;
572    }
573
574    /**
575     * Default flags for loadFile.  Subclasses may override this.
576     */
577    int loadFileFlags() {
578        return 0;
579    }
580
581    /**
582     * Load a binary class
583     */
584    boolean needsCompilation(Hashtable<ClassDeclaration, ClassDeclaration> check, ClassDeclaration c) {
585        switch (c.getStatus()) {
586
587          case CS_UNDEFINED:
588            if (tracing) dtEnter("needsCompilation: UNDEFINED " + c.getName());
589            loadDefinition(c);
590            return needsCompilation(check, c);
591
592          case CS_UNDECIDED:
593            if (tracing) dtEnter("needsCompilation: UNDECIDED " + c.getName());
594            if (check.get(c) == null) {
595                check.put(c, c);
596
597                BinaryClass bin = (BinaryClass)c.getClassDefinition();
598                for (Enumeration<ClassDeclaration> e = bin.getDependencies() ; e.hasMoreElements() ;) {
599                    ClassDeclaration dep = e.nextElement();
600                    if (needsCompilation(check, dep)) {
601                        // It must be source, dependencies need compilation
602                        c.setDefinition(bin, CS_SOURCE);
603                        if (tracing) dtExit("needsCompilation: YES (source) " + c.getName());
604                        return true;
605                    }
606                }
607            }
608            if (tracing) dtExit("needsCompilation: NO (undecided) " + c.getName());
609            return false;
610
611          case CS_BINARY:
612            if (tracing) {
613                dtEnter("needsCompilation: BINARY " + c.getName());
614                dtExit("needsCompilation: NO (binary) " + c.getName());
615            }
616            return false;
617
618        }
619
620        if (tracing) dtExit("needsCompilation: YES " + c.getName());
621        return true;
622    }
623
624    /**
625     * Load the definition of a class
626     * or at least determine how to load it.
627     * The caller must repeat calls to this method
628     * until it the state converges to CS_BINARY, CS_PARSED, or the like..
629     * @see ClassDeclaration#getClassDefinition
630     */
631    public void loadDefinition(ClassDeclaration c) {
632        if (tracing) dtEnter("loadDefinition: ENTER " +
633                             c.getName() + ", status " + c.getStatus());
634        switch (c.getStatus()) {
635          case CS_UNDEFINED: {
636            if (tracing)
637                dtEvent("loadDefinition: STATUS IS UNDEFINED");
638            Identifier nm = c.getName();
639            Package pkg;
640            try {
641                pkg = getPackage(nm.getQualifier());
642            } catch (IOException e) {
643                // If we can't get at the package, then we'll just
644                // have to set the class to be not found.
645                c.setDefinition(null, CS_NOTFOUND);
646
647                error(0, "io.exception", c);
648                if (tracing)
649                    dtExit("loadDefinition: IO EXCEPTION (package)");
650                return;
651            }
652            ClassFile binfile = pkg.getBinaryFile(nm.getName());
653            if (binfile == null) {
654                // must be source, there is no binary
655                c.setDefinition(null, CS_SOURCE);
656                if (tracing)
657                    dtExit("loadDefinition: MUST BE SOURCE (no binary) " +
658                           c.getName());
659                return;
660            }
661
662            ClassFile srcfile = pkg.getSourceFile(nm.getName());
663            if (srcfile == null) {
664                if (tracing)
665                    dtEvent("loadDefinition: NO SOURCE " + c.getName());
666                BinaryClass bc = null;
667                try {
668                    bc = loadFile(binfile);
669                } catch (IOException e) {
670                    // If we can't access the binary, set the class to
671                    // be not found.  (bug id 4030497)
672                    c.setDefinition(null, CS_NOTFOUND);
673
674                    error(0, "io.exception", binfile);
675                    if (tracing)
676                        dtExit("loadDefinition: IO EXCEPTION (binary)");
677                    return;
678                }
679                if ((bc != null) && !bc.getName().equals(nm)) {
680                    error(0, "wrong.class", binfile.getPath(), c, bc);
681                    bc = null;
682                    if (tracing)
683                        dtEvent("loadDefinition: WRONG CLASS (binary)");
684                }
685                if (bc == null) {
686                    // no source nor binary found
687                    c.setDefinition(null, CS_NOTFOUND);
688                    if (tracing)
689                        dtExit("loadDefinition: NOT FOUND (source or binary)");
690                    return;
691                }
692
693                // Couldn't find the source, try the one mentioned in the binary
694                if (bc.getSource() != null) {
695                    srcfile = ClassFile.newClassFile(new File((String)bc.getSource()));
696                    // Look for the source file
697                    srcfile = pkg.getSourceFile(srcfile.getName());
698                    if ((srcfile != null) && srcfile.exists()) {
699                        if (tracing)
700                            dtEvent("loadDefinition: FILENAME IN BINARY " +
701                                    srcfile);
702                        if (srcfile.lastModified() > binfile.lastModified()) {
703                            // must be source, it is newer than the binary
704                            c.setDefinition(bc, CS_SOURCE);
705                            if (tracing)
706                                dtEvent("loadDefinition: SOURCE IS NEWER " +
707                                        srcfile);
708                            bc.loadNested(this);
709                            if (tracing)
710                                dtExit("loadDefinition: MUST BE SOURCE " +
711                                       c.getName());
712                            return;
713                        }
714                        if (dependencies()) {
715                            c.setDefinition(bc, CS_UNDECIDED);
716                            if (tracing)
717                                dtEvent("loadDefinition: UNDECIDED " +
718                                        c.getName());
719                        } else {
720                            c.setDefinition(bc, CS_BINARY);
721                            if (tracing)
722                                dtEvent("loadDefinition: MUST BE BINARY " +
723                                        c.getName());
724                        }
725                        bc.loadNested(this);
726                        if (tracing)
727                            dtExit("loadDefinition: EXIT " +
728                                   c.getName() + ", status " + c.getStatus());
729                        return;
730                    }
731                }
732
733                // It must be binary, there is no source
734                c.setDefinition(bc, CS_BINARY);
735                if (tracing)
736                    dtEvent("loadDefinition: MUST BE BINARY (no source) " +
737                                     c.getName());
738                bc.loadNested(this);
739                if (tracing)
740                    dtExit("loadDefinition: EXIT " +
741                           c.getName() + ", status " + c.getStatus());
742                return;
743            }
744            BinaryClass bc = null;
745            try {
746                if (srcfile.lastModified() > binfile.lastModified()) {
747                    // must be source, it is newer than the binary
748                    c.setDefinition(null, CS_SOURCE);
749                    if (tracing)
750                        dtEvent("loadDefinition: MUST BE SOURCE (younger than binary) " +
751                                c.getName());
752                    return;
753                }
754                bc = loadFile(binfile);
755            } catch (IOException e) {
756                error(0, "io.exception", binfile);
757                if (tracing)
758                    dtEvent("loadDefinition: IO EXCEPTION (binary)");
759            }
760            if ((bc != null) && !bc.getName().equals(nm)) {
761                error(0, "wrong.class", binfile.getPath(), c, bc);
762                bc = null;
763                if (tracing)
764                    dtEvent("loadDefinition: WRONG CLASS (binary)");
765            }
766            if (bc != null) {
767                Identifier name = bc.getName();
768                if (name.equals(c.getName())) {
769                    if (dependencies()) {
770                        c.setDefinition(bc, CS_UNDECIDED);
771                        if (tracing)
772                            dtEvent("loadDefinition: UNDECIDED " + name);
773                    } else {
774                        c.setDefinition(bc, CS_BINARY);
775                        if (tracing)
776                            dtEvent("loadDefinition: MUST BE BINARY " + name);
777                    }
778                } else {
779                    c.setDefinition(null, CS_NOTFOUND);
780                    if (tracing)
781                        dtEvent("loadDefinition: NOT FOUND (source or binary)");
782                    if (dependencies()) {
783                        getClassDeclaration(name).setDefinition(bc, CS_UNDECIDED);
784                        if (tracing)
785                            dtEvent("loadDefinition: UNDECIDED " + name);
786                    } else {
787                        getClassDeclaration(name).setDefinition(bc, CS_BINARY);
788                        if (tracing)
789                            dtEvent("loadDefinition: MUST BE BINARY " + name);
790                    }
791                }
792            } else {
793                c.setDefinition(null, CS_NOTFOUND);
794                if (tracing)
795                    dtEvent("loadDefinition: NOT FOUND (source or binary)");
796            }
797            if (bc != null && bc == c.getClassDefinition())
798                bc.loadNested(this);
799            if (tracing) dtExit("loadDefinition: EXIT " +
800                                c.getName() + ", status " + c.getStatus());
801            return;
802          }
803
804          case CS_UNDECIDED: {
805            if (tracing) dtEvent("loadDefinition: STATUS IS UNDECIDED");
806            Hashtable<ClassDeclaration, ClassDeclaration> tab = new Hashtable<>();
807            if (!needsCompilation(tab, c)) {
808                // All undecided classes that this class depends on must be binary
809                for (Enumeration<ClassDeclaration> e = tab.keys() ; e.hasMoreElements() ; ) {
810                    ClassDeclaration dep = e.nextElement();
811                    if (dep.getStatus() == CS_UNDECIDED) {
812                        // must be binary, dependencies need compilation
813                        dep.setDefinition(dep.getClassDefinition(), CS_BINARY);
814                        if (tracing)
815                            dtEvent("loadDefinition: MUST BE BINARY " + dep);
816                    }
817                }
818            }
819            if (tracing) dtExit("loadDefinition: EXIT " +
820                                c.getName() + ", status " + c.getStatus());
821            return;
822          }
823
824          case CS_SOURCE: {
825            if (tracing) dtEvent("loadDefinition: STATUS IS SOURCE");
826            ClassFile srcfile = null;
827            Package pkg = null;
828            if (c.getClassDefinition() != null) {
829                // Use the source file name from the binary class file
830                try {
831                    pkg = getPackage(c.getName().getQualifier());
832                    srcfile = pkg.getSourceFile((String)c.getClassDefinition().getSource());
833                } catch (IOException e) {
834                    error(0, "io.exception", c);
835                    if (tracing)
836                        dtEvent("loadDefinition: IO EXCEPTION (package)");
837                }
838                if (srcfile == null) {
839                    String fn = (String)c.getClassDefinition().getSource();
840                    srcfile = ClassFile.newClassFile(new File(fn));
841                }
842            } else {
843                // Get a source file name from the package
844                Identifier nm = c.getName();
845                try {
846                    pkg = getPackage(nm.getQualifier());
847                    srcfile = pkg.getSourceFile(nm.getName());
848                } catch (IOException e)  {
849                    error(0, "io.exception", c);
850                    if (tracing)
851                        dtEvent("loadDefinition: IO EXCEPTION (package)");
852                }
853                if (srcfile == null) {
854                    // not found, there is no source
855                    c.setDefinition(null, CS_NOTFOUND);
856                    if (tracing)
857                        dtExit("loadDefinition: SOURCE NOT FOUND " +
858                               c.getName() + ", status " + c.getStatus());
859                    return;
860                }
861            }
862            try {
863                parseFile(srcfile);
864            } catch (FileNotFoundException e) {
865                error(0, "io.exception", srcfile);
866                if (tracing) dtEvent("loadDefinition: IO EXCEPTION (source)");
867            }
868            if ((c.getClassDefinition() == null) || (c.getStatus() == CS_SOURCE)) {
869                // not found after parsing the file
870                error(0, "wrong.source", srcfile.getPath(), c, pkg);
871                c.setDefinition(null, CS_NOTFOUND);
872                if (tracing)
873                    dtEvent("loadDefinition: WRONG CLASS (source) " +
874                            c.getName());
875            }
876            if (tracing) dtExit("loadDefinition: EXIT " +
877                                c.getName() + ", status " + c.getStatus());
878            return;
879          }
880        }
881        if (tracing) dtExit("loadDefinition: EXIT " +
882                            c.getName() + ", status " + c.getStatus());
883    }
884
885    /**
886     * Create a new class.
887     */
888    public ClassDefinition makeClassDefinition(Environment toplevelEnv,
889                                               long where,
890                                               IdentifierToken name,
891                                               String doc, int modifiers,
892                                               IdentifierToken superClass,
893                                               IdentifierToken interfaces[],
894                                               ClassDefinition outerClass) {
895
896        Identifier nm = name.getName();
897        long nmpos = name.getWhere();
898
899        Identifier pkgNm;
900        String mangledName = null;
901        ClassDefinition localContextClass = null;
902
903        // Provide name for a local class.  This used to be set after
904        // the class was created, but it is needed for checking within
905        // the class constructor.
906        // NOTE: It seems that we could always provide the simple name,
907        // and thereby avoid the test in 'ClassDefinition.getLocalName()'
908        // for the definedness of the local name.  There, if the local
909        // name is not set, a simple name is extracted from the result of
910        // 'getName()'.  That name can potentially change, however, as
911        // it is ultimately derived from 'ClassType.className', which is
912        // set by 'Type.changeClassName'.  Better leave this alone...
913        Identifier localName = null;
914
915        if (nm.isQualified() || nm.isInner()) {
916            pkgNm = nm;
917        } else if ((modifiers & (M_LOCAL | M_ANONYMOUS)) != 0) {
918            // Inaccessible class.  Create a name of the form
919            // 'PackageMember.N$localName' or 'PackageMember.N'.
920            // Note that the '.' will be converted later to a '$'.
921            //   pkgNm = generateName(outerClass, nm);
922            localContextClass = outerClass.getTopClass();
923            // Always use the smallest number in generating the name that
924            // renders the complete name unique within the top-level class.
925            // This is required to make the names more predictable, as part
926            // of a serialization-related workaround, and satisfies an obscure
927            // requirement that the name of a local class be of the form
928            // 'PackageMember$1$localName' when this name is unique.
929            for (int i = 1 ; ; i++) {
930                mangledName = i + (nm.equals(idNull) ? "" : SIG_INNERCLASS + nm);
931                if (localContextClass.getLocalClass(mangledName) == null) {
932                    break;
933                }
934            }
935            Identifier outerNm = localContextClass.getName();
936            pkgNm = Identifier.lookupInner(outerNm, Identifier.lookup(mangledName));
937            //System.out.println("LOCAL CLASS: " + pkgNm + " IN " + localContextClass);
938            if ((modifiers & M_ANONYMOUS) != 0) {
939                localName = idNull;
940            } else {
941                // Local class has a locally-scoped name which is independent of pkgNm.
942                localName = nm;
943            }
944        } else if (outerClass != null) {
945            // Accessible inner class.  Qualify name with surrounding class name.
946            pkgNm = Identifier.lookupInner(outerClass.getName(), nm);
947        } else {
948            pkgNm = nm;
949        }
950
951        // Find the class
952        ClassDeclaration c = toplevelEnv.getClassDeclaration(pkgNm);
953
954        // Make sure this is the first definition
955        if (c.isDefined()) {
956            toplevelEnv.error(nmpos, "class.multidef",
957                              c.getName(), c.getClassDefinition().getSource());
958            // Don't mess with the existing class declarations with same name
959            c = new ClassDeclaration (pkgNm);
960        }
961
962        if (superClass == null && !pkgNm.equals(idJavaLangObject)) {
963            superClass = new IdentifierToken(idJavaLangObject);
964        }
965
966        ClassDefinition sourceClass =
967            new SourceClass(toplevelEnv, where, c, doc,
968                            modifiers, superClass, interfaces,
969                            (SourceClass) outerClass, localName);
970
971        if (outerClass != null) {
972            // It is a member of its enclosing class.
973            outerClass.addMember(toplevelEnv, new SourceMember(sourceClass));
974            // Record local (or anonymous) class in the class whose name will
975            // serve as the prefix of the local class name.  This is necessary
976            // so that the class may be retrieved from its name, which does not
977            // fully represent the class nesting structure.
978            // See 'ClassDefinition.getClassDefinition'.
979            // This is part of a fix for bugid 4054523 and 4030421.
980            if ((modifiers & (M_LOCAL | M_ANONYMOUS)) != 0) {
981                localContextClass.addLocalClass(sourceClass, mangledName);
982            }
983        }
984
985        // The local name of an anonymous or local class used to be set here
986        // with a call to 'setLocalName'.  This has been moved to the constructor
987        // for 'SourceClass', which now takes a 'localName' argument.
988
989        return sourceClass;
990    }
991
992    /*
993     * makeMemberDefinition method is left with rawtypes and with lint messages suppressed.
994     * The addition of Generics to com.sun.tools.* has uncovered an inconsistency
995     * in usage though tools still work correctly as long as this function is allowed to
996     * function as is.
997     */
998
999    /**
1000     * Create a new field.
1001     */
1002    @SuppressWarnings({"rawtypes","unchecked"})
1003    public MemberDefinition makeMemberDefinition(Environment origEnv, long where,
1004                                               ClassDefinition clazz,
1005                                               String doc, int modifiers,
1006                                               Type type, Identifier name,
1007                                               IdentifierToken argNames[],
1008                                               IdentifierToken expIds[],
1009                                               Object value) {
1010        if (tracing) dtEvent("makeMemberDefinition: " + name + " IN " + clazz);
1011        Vector v = null;
1012        if (argNames != null) {
1013            v = new Vector(argNames.length);
1014            for (int i = 0 ; i < argNames.length ; i++) {
1015                v.addElement(argNames[i]);
1016            }
1017        }
1018        SourceMember f = new SourceMember(where, clazz, doc, modifiers,
1019                                        type, name, v, expIds, (Node)value);
1020        clazz.addMember(origEnv, f);
1021        return f;
1022    }
1023
1024    /**
1025     * Release resources in classpath.
1026     */
1027    public void shutdown() {
1028        try {
1029            if (sourcePath != null) {
1030                sourcePath.close();
1031            }
1032            if (binaryPath != null && binaryPath != sourcePath) {
1033                binaryPath.close();
1034            }
1035        } catch (IOException ee) {
1036            output(Main.getText("benv.failed_to_close_class_path",
1037                                ee.toString()));
1038        }
1039        sourcePath = null;
1040        binaryPath = null;
1041
1042        super.shutdown();
1043    }
1044
1045    /**
1046     * Error String
1047     */
1048    public
1049    String errorString(String err, Object arg1, Object arg2, Object arg3) {
1050        String key = null;
1051
1052        if(err.startsWith("warn."))
1053            key = "javac.err." + err.substring(5);
1054        else
1055            key = "javac.err." + err;
1056
1057        return Main.getText(key,
1058                            arg1 != null ? arg1.toString() : null,
1059                            arg2 != null ? arg2.toString() : null,
1060                            arg3 != null ? arg3.toString() : null);
1061    }
1062
1063    /**
1064     * The filename where the last errors have occurred
1065     */
1066    String errorFileName;
1067
1068    /**
1069     * List of outstanding error messages
1070     */
1071    ErrorMessage errors;
1072
1073    /**
1074     * Insert an error message in the list of outstanding error messages.
1075     * The list is sorted on input position and contains no duplicates.
1076     * The return value indicates whether or not the message was
1077     * actually inserted.
1078     *
1079     * The method flushErrors() used to check for duplicate error messages.
1080     * It would only detect duplicates if they were contiguous.  Removing
1081     * non-contiguous duplicate error messages is slightly less complicated
1082     * at insertion time, so the functionality was moved here.  This also
1083     * saves a miniscule number of allocations.
1084     */
1085    protected
1086    boolean insertError(long where, String message) {
1087        //output("ERR = " + message);
1088
1089        if (errors == null
1090            ||  errors.where > where) {
1091            // If the list is empty, or the error comes before any other
1092            // errors, insert it at the beginning of the list.
1093            ErrorMessage newMsg = new ErrorMessage(where, message);
1094            newMsg.next = errors;
1095            errors = newMsg;
1096
1097        } else if (errors.where == where
1098                   && errors.message.equals(message)) {
1099            // The new message is an exact duplicate of the first message
1100            // in the list.  Don't insert it.
1101            return false;
1102
1103        } else {
1104            // Okay, we know that the error doesn't come first.  Walk
1105            // the list until we find the right position for insertion.
1106            ErrorMessage current = errors;
1107            ErrorMessage next;
1108
1109            while ((next = current.next) != null
1110                   && next.where < where) {
1111                current = next;
1112            }
1113
1114            // Now walk over any errors with the same location, looking
1115            // for duplicates.  If we find a duplicate, don't insert the
1116            // error.
1117            while ((next = current.next) != null
1118                   && next.where == where) {
1119                if (next.message.equals(message)) {
1120                    // We have found an exact duplicate.  Don't bother to
1121                    // insert the error.
1122                    return false;
1123                }
1124                current = next;
1125            }
1126
1127            // Now insert after current.
1128            ErrorMessage newMsg = new ErrorMessage(where, message);
1129            newMsg.next = current.next;
1130            current.next = newMsg;
1131        }
1132
1133        // Indicate that the insertion occurred.
1134        return true;
1135    }
1136
1137    private int errorsPushed;
1138
1139    /**
1140     * Maximum number of errors to print.
1141     */
1142    public int errorLimit = 100;
1143
1144    private boolean hitErrorLimit;
1145
1146    /**
1147     * Flush outstanding errors
1148     */
1149
1150        public void pushError(String errorFileName, int line, String message,
1151                                    String referenceText, String referenceTextPointer) {
1152                int limit = errorLimit + nwarnings;
1153                if (++errorsPushed >= limit && errorLimit >= 0) {
1154                    if (!hitErrorLimit) {
1155                        hitErrorLimit = true;
1156                        output(errorString("too.many.errors",
1157                                           errorLimit,null,null));
1158                    }
1159                    return;
1160                }
1161                if (errorFileName.endsWith(".java")) {
1162                    output(errorFileName + ":" + line + ": " + message);
1163                    output(referenceText);
1164                    output(referenceTextPointer);
1165                } else {
1166                    // It wasn't really a source file (probably an error or
1167                    // warning because of a malformed or badly versioned
1168                    // class file.
1169                    output(errorFileName + ": " + message);
1170                }
1171        }
1172
1173    public void flushErrors() {
1174        if (errors == null) {
1175            return;
1176        }
1177
1178        boolean inputAvail = false;
1179        // Read the file
1180        char data[] = null;
1181        int dataLength = 0;
1182        // A malformed file encoding could cause a CharConversionException.
1183        // If something bad happens while trying to find the source file,
1184        // don't bother trying to show lines.
1185        try {
1186            FileInputStream in = new FileInputStream(errorFileName);
1187            data = new char[in.available()];
1188            InputStreamReader reader =
1189                (getCharacterEncoding() != null ?
1190                 new InputStreamReader(in, getCharacterEncoding()) :
1191                 new InputStreamReader(in));
1192            dataLength = reader.read(data);
1193            reader.close();
1194            inputAvail = true;
1195        } catch(IOException e) {
1196            // inputAvail will not be set
1197        }
1198
1199        // Report the errors
1200        for (ErrorMessage msg = errors ; msg != null ; msg = msg.next) {
1201            // There used to be code here which checked
1202            // for duplicate error messages.  This functionality
1203            // has been moved to the method insertError().  See
1204            // the comments on that method for more information.
1205
1206            int ln = (int) (msg.where >>> WHEREOFFSETBITS);
1207            int off = (int) (msg.where & ((1L << WHEREOFFSETBITS) - 1));
1208            if (off > dataLength)  off = dataLength;
1209
1210            String referenceString = "";
1211            String markerString = "";
1212            if(inputAvail) {
1213                int i, j;
1214                for (i = off ; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r') ; i--);
1215                for (j = off ; (j < dataLength) && (data[j] != '\n') && (data[j] != '\r') ; j++);
1216                referenceString = new String(data, i, j - i);
1217
1218                char strdata[] = new char[(off - i) + 1];
1219                for (j = i ; j < off ; j++) {
1220                    strdata[j-i] = (data[j] == '\t') ? '\t' : ' ';
1221                }
1222                strdata[off-i] = '^';
1223                markerString = new String(strdata);
1224            }
1225
1226            errorConsumer.pushError(errorFileName, ln, msg.message,
1227                                        referenceString, markerString);
1228        }
1229        errors = null;
1230    }
1231
1232    /**
1233     * Report error
1234     */
1235    public
1236    void reportError(Object src, long where, String err, String msg) {
1237        if (src == null) {
1238            if (errorFileName != null) {
1239                flushErrors();
1240                errorFileName = null;
1241            }
1242            if (err.startsWith("warn.")) {
1243                if (warnings()) {
1244                    nwarnings++;
1245                    output(msg);
1246                }
1247                return;
1248            }
1249            output("error: " + msg);
1250            nerrors++;
1251            flags |= F_ERRORSREPORTED;
1252
1253        } else if (src instanceof String) {
1254            String fileName = (String)src;
1255
1256            // Flush errors if we've moved on to a new file.
1257            if (!fileName.equals(errorFileName)) {
1258                flushErrors();
1259                errorFileName = fileName;
1260            }
1261
1262            // Classify `err' as a warning, deprecation warning, or
1263            // error message.  Proceed accordingly.
1264            if (err.startsWith("warn.")) {
1265                if (err.indexOf("is.deprecated") >= 0) {
1266                    // This is a deprecation warning.  Add `src' to the
1267                    // list of files with deprecation warnings.
1268                    if (!deprecationFiles.contains(src)) {
1269                        deprecationFiles.addElement(src);
1270                    }
1271
1272                    // If we are reporting deprecations, try to add it
1273                    // to our list.  Otherwise, just increment the
1274                    // deprecation count.
1275                    if (deprecation()) {
1276                        if (insertError(where, msg)) {
1277                            ndeprecations++;
1278                        }
1279                    } else {
1280                        ndeprecations++;
1281                    }
1282                } else {
1283                    // This is a regular warning.  If we are reporting
1284                    // warnings, try to add it to the list.  Otherwise, just
1285                    // increment the warning count.
1286                    if (warnings()) {
1287                        if (insertError(where, msg)) {
1288                            nwarnings++;
1289                        }
1290                    } else {
1291                        nwarnings++;
1292                    }
1293                }
1294            } else {
1295                // This is an error.  Try to add it to the list of errors.
1296                // If it isn't a duplicate, increment our error count.
1297                if (insertError(where, msg)) {
1298                    nerrors++;
1299                    flags |= F_ERRORSREPORTED;
1300                }
1301            }
1302        } else if (src instanceof ClassFile) {
1303            reportError(((ClassFile)src).getPath(), where, err, msg);
1304
1305        } else if (src instanceof Identifier) {
1306            reportError(src.toString(), where, err, msg);
1307
1308        } else if (src instanceof ClassDeclaration) {
1309            try {
1310                reportError(((ClassDeclaration)src).getClassDefinition(this), where, err, msg);
1311            } catch (ClassNotFound e) {
1312                reportError(((ClassDeclaration)src).getName(), where, err, msg);
1313            }
1314        } else if (src instanceof ClassDefinition) {
1315            ClassDefinition c = (ClassDefinition)src;
1316            if (!err.startsWith("warn.")) {
1317                c.setError();
1318            }
1319            reportError(c.getSource(), where, err, msg);
1320
1321        } else if (src instanceof MemberDefinition) {
1322            reportError(((MemberDefinition)src).getClassDeclaration(), where, err, msg);
1323
1324        } else {
1325            output(src + ":error=" + err + ":" + msg);
1326        }
1327    }
1328
1329    /**
1330     * Issue an error
1331     */
1332    public void error(Object source, long where, String err, Object arg1, Object arg2, Object arg3) {
1333        if (errorsPushed >= errorLimit + nwarnings) {
1334            // Don't bother to queue any more errors if they won't get printed.
1335            return;
1336        }
1337        if (System.getProperty("javac.dump.stack") != null) {
1338            output("javac.err."+err+": "+errorString(err, arg1, arg2, arg3));
1339            new Exception("Stack trace").printStackTrace(new PrintStream(out));
1340        }
1341        reportError(source, where, err, errorString(err, arg1, arg2, arg3));
1342    }
1343
1344    /**
1345     * Output a string. This can either be an error message or something
1346     * for debugging.
1347     */
1348    public void output(String msg) {
1349        PrintStream out =
1350            this.out instanceof PrintStream ? (PrintStream)this.out
1351                                            : new PrintStream(this.out, true);
1352        out.println(msg);
1353    }
1354}
1355