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