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