1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 package org.codehaus.groovy.control;
48
49 import org.codehaus.groovy.GroovyBugError;
50 import org.codehaus.groovy.ast.ClassNode;
51 import org.codehaus.groovy.ast.CompileUnit;
52 import org.codehaus.groovy.ast.ModuleNode;
53 import org.codehaus.groovy.classgen.AsmClassGenerator2;
54 import org.codehaus.groovy.classgen.ClassCompletionVerifier;
55 import org.codehaus.groovy.classgen.ClassGenerator;
56 import org.codehaus.groovy.classgen.GeneratorContext;
57 import org.codehaus.groovy.classgen.Verifier;
58 import org.codehaus.groovy.control.io.InputStreamReaderSource;
59 import org.codehaus.groovy.control.io.ReaderSource;
60 import org.codehaus.groovy.control.messages.ExceptionMessage;
61 import org.codehaus.groovy.control.messages.Message;
62 import org.codehaus.groovy.tools.GroovyClass;
63 import org.objectweb.asm.ClassVisitor;
64 import org.objectweb.asm.ClassWriter;
65
66 import java.io.File;
67 import java.io.FileOutputStream;
68 import java.io.IOException;
69 import java.io.InputStream;
70 import java.io.PrintWriter;
71 import java.net.MalformedURLException;
72 import java.net.URL;
73 import java.security.CodeSource;
74 import java.util.ArrayList;
75 import java.util.HashMap;
76 import java.util.Iterator;
77 import java.util.LinkedList;
78 import java.util.List;
79
80
81 /***
82 * Collects all compilation data as it is generated by the compiler system.
83 * Allows additional source units to be added and compilation run again (to
84 * affect only the deltas).
85 *
86 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
87 * @version $Id: CompilationUnit.java,v 1.8 2004/07/15 09:47:21 jstrachan Exp $
88 */
89
90 public class CompilationUnit extends ProcessingUnit {
91
92
93
94
95 protected HashMap sources;
96 protected ArrayList names;
97
98 protected CompileUnit ast;
99 protected ArrayList classes;
100
101 protected Verifier verifier;
102
103 protected ClassCompletionVerifier completionVerifier;
104
105 protected boolean debug;
106 protected boolean configured;
107
108 protected ClassgenCallback classgenCallback;
109 protected ProgressCallback progressCallback;
110
111
112 /***
113 * Initializes the CompilationUnit with defaults.
114 */
115 public CompilationUnit() {
116 this(null, null, null);
117 }
118
119
120 /***
121 * Initializes the CompilationUnit with defaults except for class loader.
122 */
123 public CompilationUnit(ClassLoader loader) {
124 this(null, null, loader);
125 }
126
127
128 /***
129 * Initializes the CompilationUnit with no security considerations.
130 */
131 public CompilationUnit(CompilerConfiguration configuration) {
132 this(configuration, null, null);
133 }
134
135
136 /***
137 * Initializes the CompilationUnit with a CodeSource for controlling
138 * security stuff and a class loader for loading classes.
139 */
140
141 public CompilationUnit(CompilerConfiguration configuration, CodeSource security, ClassLoader loader) {
142 super(configuration, loader);
143
144 this.names = new ArrayList();
145 this.sources = new HashMap();
146
147 this.ast = new CompileUnit(this.classLoader, security, this.configuration);
148 this.classes = new ArrayList();
149
150 this.verifier = new Verifier();
151 this.completionVerifier = new ClassCompletionVerifier();
152
153 this.classgenCallback = null;
154 }
155
156
157 /***
158 * Reconfigures the CompilationUnit.
159 */
160
161 public void configure(CompilerConfiguration configuration) {
162 super.configure(configuration);
163 this.debug = configuration.getDebug();
164
165
166
167
168
169
170
171 if (!this.configured && this.classLoader instanceof CompilerClassLoader) {
172 CompilerClassLoader loader = (CompilerClassLoader) this.classLoader;
173
174 Iterator iterator = configuration.getClasspath().iterator();
175 while (iterator.hasNext()) {
176 try {
177 this.configured = true;
178 loader.addPath((String) iterator.next());
179 }
180 catch (MalformedURLException e) {
181 throw new ConfigurationException(e);
182 }
183 }
184 }
185 }
186
187
188 /***
189 * Returns the CompileUnit that roots our AST.
190 */
191
192 public CompileUnit getAST() {
193 return this.ast;
194 }
195
196
197 /***
198 * Get the GroovyClasses generated by compile().
199 */
200
201 public List getClasses() {
202 return classes;
203 }
204
205
206 /***
207 * Convenience routine to get the first ClassNode, for
208 * when you are sure there is only one.
209 */
210
211 public ClassNode getFirstClassNode() {
212 return (ClassNode) ((ModuleNode) this.ast.getModules().get(0)).getClasses().get(0);
213 }
214
215
216 /***
217 * Convenience routine to get the named ClassNode.
218 */
219
220 public ClassNode getClassNode(final String name) {
221 final ClassNode[] result = new ClassNode[]{null};
222 LoopBodyForPrimaryClassNodeOperations handler = new LoopBodyForPrimaryClassNodeOperations() {
223 public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
224 if (classNode.getName().equals(name)) {
225 result[0] = classNode;
226 }
227 }
228 };
229
230 try {
231 applyToPrimaryClassNodes(handler);
232 }
233 catch (CompilationFailedException e) {
234 e.printStackTrace();
235 }
236 return result[0];
237 }
238
239
240
241
242
243
244
245
246
247 /***
248 * Adds a set of file paths to the unit.
249 */
250
251 public void addSources(String[] paths) {
252 for (int i = 0; i < paths.length; i++) {
253 File file = new File(paths[i]);
254 addSource(file);
255 }
256 }
257
258
259 /***
260 * Adds a set of source files to the unit.
261 */
262
263 public void addSources(File[] files) {
264 for (int i = 0; i < files.length; i++) {
265 addSource(files[i]);
266 }
267 }
268
269
270 /***
271 * Adds a source file to the unit.
272 */
273
274 public SourceUnit addSource(File file) {
275 return addSource(new SourceUnit(file, configuration, classLoader));
276 }
277
278
279 /***
280 * Adds a source file to the unit.
281 */
282
283 public SourceUnit addSource(URL url) {
284 return addSource(new SourceUnit(url, configuration, classLoader));
285 }
286
287
288 /***
289 * Adds a InputStream source to the unit.
290 */
291
292 public SourceUnit addSource(String name, InputStream stream) {
293 ReaderSource source = new InputStreamReaderSource(stream, configuration);
294 return addSource(new SourceUnit(name, source, configuration, classLoader));
295 }
296
297
298 /***
299 * Adds a SourceUnit to the unit.
300 */
301
302 public SourceUnit addSource(SourceUnit source) {
303 String name = source.getName();
304
305 source.setClassLoader(this.classLoader);
306
307 names.add(name);
308 sources.put(name, source);
309
310 return source;
311 }
312
313
314 /***
315 * Returns an iterator on the unit's SourceUnits.
316 */
317
318 public Iterator iterator() {
319 return new Iterator() {
320 Iterator nameIterator = names.iterator();
321
322 public boolean hasNext() {
323 return nameIterator.hasNext();
324 }
325
326 public Object next() {
327 String name = (String) nameIterator.next();
328 return sources.get(name);
329 }
330
331 public void remove() {
332 throw new UnsupportedOperationException();
333 }
334 };
335 }
336
337
338 /***
339 * Adds a ClassNode directly to the unit (ie. without source).
340 * Used primarily for testing support.
341 */
342
343 public void addClassNode(ClassNode node) {
344 ModuleNode module = new ModuleNode(this.ast);
345 this.ast.addModule(module);
346 module.addClass(node);
347 }
348
349
350
351
352
353
354
355 /***
356 * A callback interface you can use to "accompany" the classgen()
357 * code as it traverses the ClassNode tree. You will be called-back
358 * for each primary and inner class. Use setClassgenCallback() before
359 * running compile() to set your callback.
360 */
361
362 public static abstract class ClassgenCallback {
363 public abstract void call(ClassVisitor writer, ClassNode node) throws CompilationFailedException;
364 }
365
366
367 /***
368 * Sets a ClassgenCallback. You can have only one, and setting
369 * it to null removes any existing setting.
370 */
371
372 public void setClassgenCallback(ClassgenCallback visitor) {
373 this.classgenCallback = visitor;
374 }
375
376
377 /***
378 * A callback interface you can use to get a callback after every
379 * unit of the compile process. You will be called-back with a
380 * ProcessingUnit and a phase indicator. Use setProgressCallback()
381 * before running compile() to set your callback.
382 */
383
384 public static abstract class ProgressCallback {
385 public abstract void call(ProcessingUnit context, int phase) throws CompilationFailedException;
386 }
387
388
389 /***
390 * Sets a ProgressCallback. You can have only one, and setting
391 * it to null removes any existing setting.
392 */
393
394 public void setProgressCallback(ProgressCallback callback) {
395 this.progressCallback = callback;
396 }
397
398
399
400
401
402
403
404
405 /***
406 * Synonym for compile(Phases.ALL).
407 */
408
409 public void compile() throws CompilationFailedException {
410 compile(Phases.ALL);
411 }
412
413
414 /***
415 * Compiles the compilation unit from sources.
416 */
417
418 public void compile(int throughPhase) throws CompilationFailedException {
419
420
421
422
423
424 gotoPhase(Phases.INITIALIZATION);
425
426 do {
427 if (throughPhase < Phases.PARSING) {
428 break;
429 }
430
431 gotoPhase(Phases.PARSING);
432 parse();
433
434 if (throughPhase < Phases.CONVERSION) {
435 break;
436 }
437
438 gotoPhase(Phases.CONVERSION);
439 convert();
440
441 if (throughPhase < Phases.CLASS_GENERATION) {
442 break;
443 }
444
445 gotoPhase(Phases.CLASS_GENERATION);
446 classgen();
447
448 if (throughPhase < Phases.OUTPUT) {
449 break;
450 }
451
452 gotoPhase(Phases.OUTPUT);
453 output();
454
455 if (throughPhase < Phases.FINALIZATION) {
456 break;
457 }
458
459 gotoPhase(Phases.FINALIZATION);
460
461 }
462 while (false);
463
464 }
465
466
467 /***
468 * Parses all sources.
469 */
470
471 public void parse() throws CompilationFailedException {
472 if (this.phase != Phases.PARSING) {
473 throw new GroovyBugError("CompilationUnit not read for parse()");
474 }
475
476 applyToSourceUnits(parse);
477
478 completePhase();
479 applyToSourceUnits(mark);
480 }
481
482
483 /***
484 * Runs parse() on a single SourceUnit.
485 */
486
487 private LoopBodyForSourceUnitOperations parse = new LoopBodyForSourceUnitOperations() {
488 public void call(SourceUnit source) throws CompilationFailedException {
489 source.parse();
490
491 if (CompilationUnit.this.progressCallback != null) {
492 CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase);
493 }
494 }
495 };
496
497
498 /***
499 * Builds ASTs for all parsed sources.
500 */
501
502 public void convert() throws CompilationFailedException {
503 if (this.phase != Phases.CONVERSION) {
504 throw new GroovyBugError("CompilationUnit not ready for convert()");
505 }
506
507 applyToSourceUnits(convert);
508
509 completePhase();
510 applyToSourceUnits(mark);
511 }
512
513
514 /***
515 * Runs convert() on a single SourceUnit.
516 */
517
518 private LoopBodyForSourceUnitOperations convert = new LoopBodyForSourceUnitOperations() {
519 public void call(SourceUnit source) throws CompilationFailedException {
520 source.convert();
521 CompilationUnit.this.ast.addModule(source.getAST());
522
523 if (CompilationUnit.this.progressCallback != null) {
524 CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase);
525 }
526 }
527 };
528
529
530 /***
531 * Expands and canonicalizes the ASTs generated during
532 * parsing and conversion, then generates classes.
533 */
534
535 public void classgen() throws CompilationFailedException {
536 if (this.phase != Phases.CLASS_GENERATION) {
537 throw new GroovyBugError("CompilationUnit not ready for classgen()");
538 }
539
540 applyToPrimaryClassNodes(classgen);
541
542 completePhase();
543 applyToSourceUnits(mark);
544
545
546
547
548
549 if (this.progressCallback != null) {
550 this.progressCallback.call(this, CompilationUnit.this.phase);
551 }
552
553 }
554
555
556 /***
557 * Runs classgen() on a single ClassNode.
558 */
559
560 private LoopBodyForPrimaryClassNodeOperations classgen = new LoopBodyForPrimaryClassNodeOperations() {
561 public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
562
563
564
565 verifier.visitClass(classNode);
566
567
568
569
570
571 ClassVisitor visitor = createClassVisitor();
572
573 String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName());
574 ClassGenerator generator = new AsmClassGenerator2(context, visitor, classLoader, sourceName);
575
576
577
578
579 generator.visitClass(classNode);
580 completionVerifier.visitClass(classNode);
581
582 if (!debug) {
583 byte[] bytes = ((ClassWriter) visitor).toByteArray();
584
585 }
586
587
588
589
590
591
592
593 if (CompilationUnit.this.classgenCallback != null) {
594 if (debug) {
595 try {
596 classgenCallback.call(visitor, classNode);
597 }
598 catch (Throwable t) {
599 output.println("Classgen callback threw: " + t);
600 t.printStackTrace(output);
601 }
602 }
603 else {
604 classgenCallback.call(visitor, classNode);
605 }
606 }
607
608
609
610
611
612 LinkedList innerClasses = generator.getInnerClasses();
613 while (!innerClasses.isEmpty()) {
614 classgen.call(source, context, (ClassNode) innerClasses.removeFirst());
615 }
616
617 }
618
619 };
620
621 protected ClassVisitor createClassVisitor() {
622 /*** avoid runtime dependency on asm util
623 ClassVisitor visitor;
624 if( debug )
625 {
626 visitor = new DumpClassVisitor(output);
627 }
628 else
629
630 {
631 visitor = new ClassWriter(true);
632 }
633 return visitor;
634 */
635 return new ClassWriter(true);
636 }
637
638
639 /***
640 * Outputs the generated class files to permanent storage.
641 */
642
643 public void output() throws CompilationFailedException {
644 if (this.phase != Phases.OUTPUT && !(this.phase == Phases.CLASS_GENERATION && this.phaseComplete)) {
645 throw new GroovyBugError("CompilationUnit not ready for output()");
646 }
647
648 boolean failures = false;
649
650 Iterator iterator = this.classes.iterator();
651 while (iterator.hasNext()) {
652
653
654
655 GroovyClass gclass = (GroovyClass) iterator.next();
656 String name = gclass.getName().replace('.', File.separatorChar) + ".class";
657 File path = new File(configuration.getTargetDirectory(), name);
658
659
660
661
662 File directory = path.getParentFile();
663 if (directory != null && !directory.exists()) {
664 directory.mkdirs();
665 }
666
667
668
669
670 byte[] bytes = gclass.getBytes();
671
672 FileOutputStream stream = null;
673 try {
674 stream = new FileOutputStream(path);
675 stream.write(bytes, 0, bytes.length);
676 }
677 catch (IOException e) {
678 addError(Message.create(e.getMessage()));
679 failures = true;
680 }
681 finally {
682 if (stream != null) {
683 try {
684 stream.close();
685 }
686 catch (Exception e) {
687 }
688 }
689 }
690 }
691
692 if (failures) {
693 fail();
694 }
695
696 completePhase();
697 applyToSourceUnits(mark);
698
699
700
701
702
703 if (CompilationUnit.this.progressCallback != null) {
704 CompilationUnit.this.progressCallback.call(this, this.phase);
705 }
706 }
707
708
709 /***
710 * Returns true if there are any errors pending.
711 */
712
713 public boolean hasErrors() {
714 boolean hasErrors = false;
715
716 Iterator keys = names.iterator();
717 while (keys.hasNext()) {
718 String name = (String) keys.next();
719 SourceUnit source = (SourceUnit) sources.get(name);
720
721 if (source.hasErrors()) {
722 hasErrors = true;
723 break;
724 }
725 }
726
727 return hasErrors || super.hasErrors();
728 }
729
730
731
732
733
734
735
736
737 /***
738 * Updates the phase marker on all sources.
739 */
740
741 protected void mark() throws CompilationFailedException {
742 applyToSourceUnits(mark);
743 }
744
745
746 /***
747 * Marks a single SourceUnit with the current phase,
748 * if it isn't already there yet.
749 */
750
751 private LoopBodyForSourceUnitOperations mark = new LoopBodyForSourceUnitOperations() {
752 public void call(SourceUnit source) throws CompilationFailedException {
753 if (source.phase < phase) {
754 source.gotoPhase(phase);
755 }
756
757 if (source.phase == phase && phaseComplete && !source.phaseComplete) {
758 source.completePhase();
759 }
760 }
761 };
762
763
764
765
766
767
768
769 /***
770 * An callback interface for use in the applyToSourceUnits loop driver.
771 */
772
773 public abstract class LoopBodyForSourceUnitOperations {
774 public abstract void call(SourceUnit source) throws CompilationFailedException;
775 }
776
777
778 /***
779 * A loop driver for applying operations to all SourceUnits.
780 * Automatically skips units that have already been processed
781 * through the current phase.
782 */
783
784 public void applyToSourceUnits(LoopBodyForSourceUnitOperations body) throws CompilationFailedException {
785 boolean failures = false;
786
787 Iterator keys = names.iterator();
788 while (keys.hasNext()) {
789 String name = (String) keys.next();
790 SourceUnit source = (SourceUnit) sources.get(name);
791 if (source.phase <= phase) {
792 try {
793 body.call(source);
794 }
795 catch (CompilationFailedException e) {
796 failures = true;
797 }
798 catch (Exception e) {
799 throw new GroovyBugError(e);
800 }
801 }
802 }
803
804 if (failures) {
805 fail();
806 }
807 }
808
809
810
811
812
813
814
815
816 /***
817 * An callback interface for use in the applyToSourceUnits loop driver.
818 */
819
820 public abstract class LoopBodyForPrimaryClassNodeOperations {
821 public abstract void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException;
822 }
823
824
825 /***
826 * A loop driver for applying operations to all primary ClassNodes in
827 * our AST. Automatically skips units that have already been processed
828 * through the current phase.
829 */
830
831 public void applyToPrimaryClassNodes(LoopBodyForPrimaryClassNodeOperations body) throws CompilationFailedException {
832 boolean failures = false;
833
834 Iterator modules = this.ast.getModules().iterator();
835 while (modules.hasNext()) {
836 ModuleNode module = (ModuleNode) modules.next();
837
838 try {
839 Iterator classNodes = module.getClasses().iterator();
840 while (classNodes.hasNext()) {
841 ClassNode classNode = (ClassNode) classNodes.next();
842 SourceUnit context = module.getContext();
843 if (context == null || context.phase <= phase) {
844 body.call(module.getContext(), new GeneratorContext(this.ast), classNode);
845 }
846 }
847 }
848 catch (CompilationFailedException e) {
849 e.printStackTrace();
850 failures = true;
851 addError(new ExceptionMessage(e));
852 }
853 catch (Exception e) {
854 failures = true;
855 e.printStackTrace();
856
857
858
859
860
861
862 addError(new ExceptionMessage(e));
863 }
864 }
865
866 if (failures) {
867 fail();
868 }
869 }
870
871
872
873
874
875
876
877
878 /***
879 * Writes error messages to the specified PrintWriter.
880 */
881
882 public void write(PrintWriter writer, Janitor janitor) {
883 super.write(writer, janitor);
884
885 Iterator keys = names.iterator();
886 while (keys.hasNext()) {
887 String name = (String) keys.next();
888 SourceUnit source = (SourceUnit) sources.get(name);
889
890 if (source.hasErrors()) {
891 source.write(writer, janitor);
892 }
893 }
894
895 }
896
897
898 }
899
900
901
902