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
48
49
50
51
52
53
54 package org.codehaus.groovy.control;
55
56 import java.io.File;
57 import java.io.FileOutputStream;
58 import java.io.IOException;
59 import java.io.InputStream;
60 import java.net.URL;
61 import java.security.CodeSource;
62 import java.util.*;
63
64 import org.codehaus.groovy.GroovyBugError;
65 import org.codehaus.groovy.ast.ASTNode;
66 import org.codehaus.groovy.ast.ClassNode;
67 import org.codehaus.groovy.ast.CompileUnit;
68 import org.codehaus.groovy.ast.ModuleNode;
69 import org.codehaus.groovy.classgen.AsmClassGenerator;
70 import org.codehaus.groovy.classgen.ClassCompletionVerifier;
71 import org.codehaus.groovy.classgen.ClassGenerator;
72 import org.codehaus.groovy.classgen.GeneratorContext;
73 import org.codehaus.groovy.classgen.VariableScopeVisitor;
74 import org.codehaus.groovy.classgen.Verifier;
75 import org.codehaus.groovy.control.io.InputStreamReaderSource;
76 import org.codehaus.groovy.control.io.ReaderSource;
77 import org.codehaus.groovy.control.messages.ExceptionMessage;
78 import org.codehaus.groovy.control.messages.Message;
79 import org.codehaus.groovy.control.messages.SimpleMessage;
80 import org.codehaus.groovy.syntax.SyntaxException;
81 import org.codehaus.groovy.syntax.ClassSource;
82 import org.codehaus.groovy.syntax.SourceSummary;
83 import org.codehaus.groovy.tools.GroovyClass;
84 import org.objectweb.asm.ClassVisitor;
85 import org.objectweb.asm.ClassWriter;
86
87 import groovy.lang.GroovyClassLoader;
88 import groovy.lang.GroovyRuntimeException;
89
90 /***
91 * Collects all compilation data as it is generated by the compiler system.
92 * Allows additional source units to be added and compilation run again (to
93 * affect only the deltas).
94 *
95 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
96 * @version $Id: CompilationUnit.java,v 1.42 2006/06/06 14:33:47 blackdrag Exp $
97 */
98
99 public class CompilationUnit extends ProcessingUnit {
100
101
102
103
104
105
106 protected HashMap sources;
107 protected Map summariesBySourceName;
108 protected Map summariesByPublicClassName;
109 protected Map classSourcesByPublicClassName;
110 protected ArrayList names;
111 protected LinkedList queuedSources;
112
113
114 protected CompileUnit ast;
115 protected ArrayList generatedClasses;
116
117
118 protected Verifier verifier;
119
120
121 protected boolean debug;
122 protected boolean configured;
123
124
125 protected ClassgenCallback classgenCallback;
126 protected ProgressCallback progressCallback;
127 protected ResolveVisitor resolveVisitor;
128
129 LinkedList[] phaseOperations;
130
131
132 /***
133 * Initializes the CompilationUnit with defaults.
134 */
135 public CompilationUnit() {
136 this(null, null, null);
137 }
138
139
140
141 /***
142 * Initializes the CompilationUnit with defaults except for class loader.
143 */
144 public CompilationUnit(GroovyClassLoader loader) {
145 this(null, null, loader);
146 }
147
148
149
150 /***
151 * Initializes the CompilationUnit with no security considerations.
152 */
153 public CompilationUnit(CompilerConfiguration configuration) {
154 this(configuration, null, null);
155 }
156
157 /***
158 * Initializes the CompilationUnit with a CodeSource for controlling
159 * security stuff and a class loader for loading classes.
160 */
161 public CompilationUnit(CompilerConfiguration configuration, CodeSource security, GroovyClassLoader loader) {
162 super(configuration, loader, null);
163 this.names = new ArrayList();
164 this.queuedSources = new LinkedList();
165 this.sources = new HashMap();
166 this.summariesBySourceName = new HashMap();
167 this.summariesByPublicClassName = new HashMap();
168 this.classSourcesByPublicClassName = new HashMap();
169
170 this.ast = new CompileUnit(this.classLoader, security, this.configuration);
171 this.generatedClasses = new ArrayList();
172
173
174 this.verifier = new Verifier();
175 this.resolveVisitor = new ResolveVisitor(this);
176
177 phaseOperations = new LinkedList[Phases.ALL+1];
178 for (int i=0; i<phaseOperations.length; i++) {
179 phaseOperations[i] = new LinkedList();
180 }
181 addPhaseOperation(new SourceUnitOperation() {
182 public void call(SourceUnit source) throws CompilationFailedException {
183 source.parse();
184 }
185 }, Phases.PARSING);
186 addPhaseOperation(summarize, Phases.PARSING);
187 addPhaseOperation(convert, Phases.CONVERSION);
188 addPhaseOperation(resolve, Phases.SEMANTIC_ANALYSIS);
189 addPhaseOperation(classgen, Phases.CLASS_GENERATION);
190 addPhaseOperation(output);
191
192 this.classgenCallback = null;
193 }
194
195
196
197
198
199 public void addPhaseOperation(SourceUnitOperation op, int phase) {
200 if (phase<0 || phase>Phases.ALL) throw new IllegalArgumentException("phase "+phase+" is unknown");
201 phaseOperations[phase].add(op);
202 }
203
204 public void addPhaseOperation(PrimaryClassNodeOperation op, int phase) {
205 if (phase<0 || phase>Phases.ALL) throw new IllegalArgumentException("phase "+phase+" is unknown");
206 phaseOperations[phase].add(op);
207 }
208
209 public void addPhaseOperation(GroovyClassOperation op) {
210 phaseOperations[Phases.OUTPUT].addFirst(op);
211 }
212
213
214 /***
215 * Configures its debugging mode and classloader classpath from a given compiler configuration.
216 * This cannot be done more than once due to limitations in {@link java.net.URLClassLoader URLClassLoader}.
217 */
218 public void configure(CompilerConfiguration configuration) {
219 super.configure(configuration);
220 this.debug = configuration.getDebug();
221
222 if (!this.configured && this.classLoader instanceof GroovyClassLoader) {
223 appendCompilerConfigurationClasspathToClassLoader(configuration, (GroovyClassLoader) this.classLoader);
224 }
225
226 this.configured = true;
227 }
228
229 private void appendCompilerConfigurationClasspathToClassLoader(CompilerConfiguration configuration, GroovyClassLoader classLoader) {
230
231
232
233 }
234
235 /***
236 * Returns the CompileUnit that roots our AST.
237 */
238 public CompileUnit getAST() {
239 return this.ast;
240 }
241
242 /***
243 * Get the source summaries
244 */
245 public Map getSummariesBySourceName() {
246 return summariesBySourceName;
247 }
248 public Map getSummariesByPublicClassName() {
249 return summariesByPublicClassName;
250 }
251 public Map getClassSourcesByPublicClassName() {
252 return classSourcesByPublicClassName;
253 }
254
255 public boolean isPublicClass(String className) {
256 return summariesByPublicClassName.containsKey(className);
257 }
258
259
260 /***
261 * Get the GroovyClasses generated by compile().
262 */
263 public List getClasses() {
264 return generatedClasses;
265 }
266
267
268 /***
269 * Convenience routine to get the first ClassNode, for
270 * when you are sure there is only one.
271 */
272 public ClassNode getFirstClassNode() {
273 return (ClassNode) ((ModuleNode) this.ast.getModules().get(0)).getClasses().get(0);
274 }
275
276
277 /***
278 * Convenience routine to get the named ClassNode.
279 */
280 public ClassNode getClassNode(final String name) {
281 final ClassNode[] result = new ClassNode[]{null};
282 PrimaryClassNodeOperation handler = new PrimaryClassNodeOperation() {
283 public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
284 if (classNode.getName().equals(name)) {
285 result[0] = classNode;
286 }
287 }
288 };
289
290 try {
291 applyToPrimaryClassNodes(handler);
292 } catch (CompilationFailedException e) {
293 if (debug) e.printStackTrace();
294 }
295 return result[0];
296 }
297
298
299
300
301
302
303
304
305
306 /***
307 * Adds a set of file paths to the unit.
308 */
309 public void addSources(String[] paths) {
310 for (int i = 0; i < paths.length; i++) {
311 File file = new File(paths[i]);
312 addSource(file);
313 }
314 }
315
316
317 /***
318 * Adds a set of source files to the unit.
319 */
320 public void addSources(File[] files) {
321 for (int i = 0; i < files.length; i++) {
322 addSource(files[i]);
323 }
324 }
325
326
327 /***
328 * Adds a source file to the unit.
329 */
330 public SourceUnit addSource(File file) {
331 return addSource(new SourceUnit(file, configuration, classLoader, getErrorCollector()));
332 }
333
334 /***
335 * Adds a source file to the unit.
336 */
337 public SourceUnit addSource(URL url) {
338 return addSource(new SourceUnit(url, configuration, classLoader,getErrorCollector()));
339 }
340
341
342 /***
343 * Adds a InputStream source to the unit.
344 */
345 public SourceUnit addSource(String name, InputStream stream) {
346 ReaderSource source = new InputStreamReaderSource(stream, configuration);
347 return addSource(new SourceUnit(name, source, configuration, classLoader, getErrorCollector()));
348 }
349
350
351 /***
352 * Adds a SourceUnit to the unit.
353 */
354 public SourceUnit addSource(SourceUnit source) {
355 String name = source.getName();
356 source.setClassLoader(this.classLoader);
357 for (Iterator iter = queuedSources.iterator(); iter.hasNext();) {
358 SourceUnit su = (SourceUnit) iter.next();
359 if (name.equals(su.getName())) return su;
360 }
361 queuedSources.add(source);
362 return source;
363 }
364
365
366 /***
367 * Returns an iterator on the unit's SourceUnits.
368 */
369 public Iterator iterator() {
370 return new Iterator() {
371 Iterator nameIterator = names.iterator();
372
373
374 public boolean hasNext() {
375 return nameIterator.hasNext();
376 }
377
378
379 public Object next() {
380 String name = (String) nameIterator.next();
381 return sources.get(name);
382 }
383
384
385 public void remove() {
386 throw new UnsupportedOperationException();
387 }
388 };
389 }
390
391
392 /***
393 * Adds a ClassNode directly to the unit (ie. without source).
394 * WARNING: the source is needed for error reporting, using
395 * this method without setting a SourceUnit will cause
396 * NullPinterExceptions
397 */
398 public void addClassNode(ClassNode node) {
399 ModuleNode module = new ModuleNode(this.ast);
400 this.ast.addModule(module);
401 module.addClass(node);
402 }
403
404
405
406
407
408
409 /***
410 * A callback interface you can use to "accompany" the classgen()
411 * code as it traverses the ClassNode tree. You will be called-back
412 * for each primary and inner class. Use setClassgenCallback() before
413 * running compile() to set your callback.
414 */
415 public static abstract class ClassgenCallback {
416 public abstract void call(ClassVisitor writer, ClassNode node) throws CompilationFailedException;
417 }
418
419
420 /***
421 * Sets a ClassgenCallback. You can have only one, and setting
422 * it to null removes any existing setting.
423 */
424 public void setClassgenCallback(ClassgenCallback visitor) {
425 this.classgenCallback = visitor;
426 }
427
428
429 /***
430 * A callback interface you can use to get a callback after every
431 * unit of the compile process. You will be called-back with a
432 * ProcessingUnit and a phase indicator. Use setProgressCallback()
433 * before running compile() to set your callback.
434 */
435 public static abstract class ProgressCallback {
436
437 public abstract void call(ProcessingUnit context, int phase) throws CompilationFailedException;
438 }
439
440 /***
441 * Sets a ProgressCallback. You can have only one, and setting
442 * it to null removes any existing setting.
443 */
444 public void setProgressCallback(ProgressCallback callback) {
445 this.progressCallback = callback;
446 }
447
448
449
450
451
452
453 /***
454 * Synonym for compile(Phases.ALL).
455 */
456 public void compile() throws CompilationFailedException {
457 compile(Phases.ALL);
458 }
459
460 /***
461 * Compiles the compilation unit from sources.
462 */
463 public void compile(int throughPhase) throws CompilationFailedException {
464
465
466
467
468 gotoPhase(Phases.INITIALIZATION);
469 throughPhase = Math.min(throughPhase,Phases.ALL);
470
471 while (throughPhase >= phase && phase <= Phases.ALL) {
472
473 for (Iterator it = phaseOperations[phase].iterator(); it.hasNext();) {
474 Object operation = it.next();
475 if (operation instanceof PrimaryClassNodeOperation) {
476 applyToPrimaryClassNodes((PrimaryClassNodeOperation) operation);
477 } else if (operation instanceof SourceUnitOperation) {
478 applyToSourceUnits((SourceUnitOperation)operation);
479 } else {
480 applyToGeneratedGroovyClasses((GroovyClassOperation)operation);
481 }
482 }
483
484 if (dequeued()) continue;
485
486 if ( phase==Phases.CLASS_GENERATION &&
487 ast.hasClassNodeToCompile()) {
488 break;
489 }
490 if (progressCallback != null) progressCallback.call(this, phase);
491 completePhase();
492 applyToSourceUnits(mark);
493
494 gotoPhase(phase+1);
495
496 if (phase==Phases.CLASS_GENERATION) {
497 sortClasses();
498 }
499 }
500
501 for (Iterator iter = ast.iterateClassNodeToCompile(); iter.hasNext();) {
502 String name = (String) iter.next();
503 String location = ast.getScriptSourceLocation(name);
504 getErrorCollector().addErrorAndContinue(
505 new SimpleMessage("Compilation incomplete: expected to find the class "+name+" in "+location,this)
506 );
507 }
508 errorCollector.failIfErrors();
509 }
510
511 private void sortClasses() throws CompilationFailedException {
512 Iterator modules = this.ast.getModules().iterator();
513 while (modules.hasNext()) {
514 ModuleNode module = (ModuleNode) modules.next();
515
516
517
518 List classes = module.getClasses();
519 for (Iterator iter = classes.iterator(); iter.hasNext();) {
520 ClassNode start = (ClassNode) iter.next();
521 ClassNode cn = start;
522 HashSet parents = new HashSet();
523 do {
524 if (parents.contains(cn.getName())) {
525 getErrorCollector().addErrorAndContinue(
526 new SimpleMessage("cyclic inheritance involving "+cn.getName()+" in class "+start.getName(),this)
527 );
528 cn=null;
529 } else {
530 parents.add(cn.getName());
531 cn = cn.getSuperClass();
532 }
533 } while (cn!=null);
534 }
535 errorCollector.failIfErrors();
536 module.sortClasses();
537
538 }
539 }
540
541
542 /***
543 * Dequeues any source units add through addSource and resets the compiler phase
544 * to initialization.
545 *
546 * Note: this does not mean a file is recompiled. If a SoucreUnit has already passed
547 * a phase it is skipped until a higher phase is reached.
548 * @return TODO
549 *
550 * @throws CompilationFailedException
551 */
552 protected boolean dequeued() throws CompilationFailedException {
553 boolean dequeue = !queuedSources.isEmpty();
554 while (!queuedSources.isEmpty()) {
555 SourceUnit su = (SourceUnit) queuedSources.removeFirst();
556 String name = su.getName();
557 names.add(name);
558 sources.put(name,su);
559 }
560 if (dequeue) {
561 gotoPhase(Phases.INITIALIZATION);
562 }
563 return dequeue;
564 }
565
566
567 /***
568 * Adds summary of each class to maps
569 */
570 private SourceUnitOperation summarize = new SourceUnitOperation() {
571 public void call(SourceUnit source) throws CompilationFailedException {
572 SourceSummary sourceSummary = source.getSourceSummary();
573 if (sourceSummary != null) {
574 summariesBySourceName.put(source.getName(),sourceSummary);
575 List publicClassSources = sourceSummary.getPublicClassSources();
576 if (publicClassSources == null || publicClassSources.size() == 0) {
577
578 summariesByPublicClassName.put("*NoName*",sourceSummary);
579
580 } else {
581 Iterator itr = publicClassSources.iterator();
582 while (itr.hasNext()) {
583 ClassSource classSource = (ClassSource)itr.next();
584 summariesByPublicClassName.put(classSource.getName(),sourceSummary);
585 classSourcesByPublicClassName.put(classSource.getName(),classSource);
586 }
587 }
588 }
589 }
590 };
591
592 /***
593 * Resolves all types
594 */
595 private SourceUnitOperation resolve = new SourceUnitOperation() {
596 public void call(SourceUnit source) throws CompilationFailedException {
597 List classes = source.ast.getClasses();
598 for (Iterator it = classes.iterator(); it.hasNext();) {
599 ClassNode node = (ClassNode) it.next();
600
601 VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source);
602 scopeVisitor.visitClass(node);
603
604 resolveVisitor.startResolving(node,source);
605 }
606
607 }
608 };
609
610 /***
611 * Runs convert() on a single SourceUnit.
612 */
613 private SourceUnitOperation convert = new SourceUnitOperation() {
614 public void call(SourceUnit source) throws CompilationFailedException {
615 source.convert();
616 CompilationUnit.this.ast.addModule(source.getAST());
617
618
619 if (CompilationUnit.this.progressCallback != null) {
620 CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase);
621 }
622 }
623 };
624
625 private GroovyClassOperation output = new GroovyClassOperation() {
626 public void call(GroovyClass gclass) throws CompilationFailedException {
627 boolean failures = false;
628 String name = gclass.getName().replace('.', File.separatorChar) + ".class";
629 File path = new File(configuration.getTargetDirectory(), name);
630
631
632
633
634 File directory = path.getParentFile();
635 if (directory != null && !directory.exists()) {
636 directory.mkdirs();
637 }
638
639
640
641
642 byte[] bytes = gclass.getBytes();
643
644 FileOutputStream stream = null;
645 try {
646 stream = new FileOutputStream(path);
647 stream.write(bytes, 0, bytes.length);
648 } catch (IOException e) {
649 getErrorCollector().addError(Message.create(e.getMessage(),CompilationUnit.this));
650 failures = true;
651 } finally {
652 if (stream != null) {
653 try {
654 stream.close();
655 } catch (Exception e) {
656 }
657 }
658 }
659 }
660 };
661
662
663 /***
664 * Runs classgen() on a single ClassNode.
665 */
666 private PrimaryClassNodeOperation classgen = new PrimaryClassNodeOperation() {
667 public boolean needSortedInput() {
668 return true;
669 }
670 public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
671
672
673
674
675 try {
676 verifier.visitClass(classNode);
677 } catch (GroovyRuntimeException rpe) {
678 ASTNode node = rpe.getNode();
679 getErrorCollector().addError(
680 new SyntaxException(rpe.getMessage(),null,node.getLineNumber(),node.getColumnNumber()),
681 source
682 );
683 }
684
685 LabelVerifier lv = new LabelVerifier(source);
686 lv.visitClass(classNode);
687
688 ClassCompletionVerifier completionVerifier = new ClassCompletionVerifier(source);
689 completionVerifier.visitClass(classNode);
690
691
692
693
694 getErrorCollector().failIfErrors();
695
696
697
698
699 ClassVisitor visitor = createClassVisitor();
700
701
702 String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName());
703
704
705 if (sourceName != null)
706 sourceName = sourceName.substring(Math.max(sourceName.lastIndexOf('//'), sourceName.lastIndexOf('/')) + 1);
707 ClassGenerator generator = new AsmClassGenerator(context, visitor, classLoader, sourceName);
708
709
710
711
712
713 generator.visitClass(classNode);
714
715
716 byte[] bytes = ((ClassWriter) visitor).toByteArray();
717 generatedClasses.add(new GroovyClass(classNode.getName(), bytes));
718
719
720
721
722
723 if (CompilationUnit.this.classgenCallback != null) {
724 classgenCallback.call(visitor, classNode);
725 }
726
727
728
729
730
731 LinkedList innerClasses = generator.getInnerClasses();
732 while (!innerClasses.isEmpty()) {
733 classgen.call(source, context, (ClassNode) innerClasses.removeFirst());
734 }
735 }
736 };
737
738
739 protected ClassVisitor createClassVisitor() {
740 return new ClassWriter(true);
741 }
742
743
744
745
746
747 /***
748 * Updates the phase marker on all sources.
749 */
750 protected void mark() throws CompilationFailedException {
751 applyToSourceUnits(mark);
752 }
753
754
755 /***
756 * Marks a single SourceUnit with the current phase,
757 * if it isn't already there yet.
758 */
759 private SourceUnitOperation mark = new SourceUnitOperation() {
760 public void call(SourceUnit source) throws CompilationFailedException {
761 if (source.phase < phase) {
762 source.gotoPhase(phase);
763 }
764
765
766 if (source.phase == phase && phaseComplete && !source.phaseComplete) {
767 source.completePhase();
768 }
769 }
770 };
771
772
773
774
775
776
777
778
779
780 /***
781 * An callback interface for use in the applyToSourceUnits loop driver.
782 */
783 public static abstract class SourceUnitOperation {
784 public abstract void call(SourceUnit source) throws CompilationFailedException;
785 }
786
787
788 /***
789 * A loop driver for applying operations to all SourceUnits.
790 * Automatically skips units that have already been processed
791 * through the current phase.
792 */
793 public void applyToSourceUnits(SourceUnitOperation body) throws CompilationFailedException {
794 Iterator keys = names.iterator();
795 while (keys.hasNext()) {
796 String name = (String) keys.next();
797 SourceUnit source = (SourceUnit) sources.get(name);
798 if ( (source.phase < phase) || (source.phase == phase && !source.phaseComplete)) {
799 try {
800 body.call(source);
801 } catch (CompilationFailedException e) {
802 throw e;
803 } catch (Exception e) {
804 GroovyBugError gbe = new GroovyBugError(e);
805 changeBugText(gbe,source);
806 throw gbe;
807 } catch (GroovyBugError e) {
808 changeBugText(e,source);
809 throw e;
810 }
811 }
812 }
813
814
815 getErrorCollector().failIfErrors();
816 }
817
818
819
820
821
822
823
824 /***
825 * An callback interface for use in the applyToSourceUnits loop driver.
826 */
827 public static abstract class PrimaryClassNodeOperation {
828 public abstract void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException;
829 public boolean needSortedInput(){
830 return false;
831 }
832 }
833
834 public static abstract class GroovyClassOperation {
835 public abstract void call(GroovyClass gclass) throws CompilationFailedException;
836 }
837
838 private List getPrimaryClassNodes(boolean sort) {
839 ArrayList unsorted = new ArrayList();
840 Iterator modules = this.ast.getModules().iterator();
841 while (modules.hasNext()) {
842 ModuleNode module = (ModuleNode) modules.next();
843
844 Iterator classNodes = module.getClasses().iterator();
845 while (classNodes.hasNext()) {
846 ClassNode classNode = (ClassNode) classNodes.next();
847 unsorted.add(classNode);
848 }
849 }
850
851 if(sort==false) return unsorted;
852
853 int[] index = new int[unsorted.size()];
854 {
855 int i = 0;
856 for (Iterator iter = unsorted.iterator(); iter.hasNext(); i++) {
857 ClassNode element = (ClassNode) iter.next();
858 int count = 0;
859 while (element!=null){
860 count++;
861 element = element.getSuperClass();
862 }
863 index[i] = count;
864 }
865 }
866
867 ArrayList sorted = new ArrayList(unsorted.size());
868 int start = 0;
869 for (int i=0; i<index.length; i++) {
870 int min = -1;
871 for (int j=0; j<index.length; j++) {
872 if (index[j]==-1) continue;
873 if (min==-1) {
874 min = j;
875 } else if (index[j]<index[min]) {
876 min = j;
877 }
878 }
879 sorted.add(unsorted.get(min));
880 index[min] = -1;
881 }
882
883 return sorted;
884 }
885
886 /***
887 * A loop driver for applying operations to all primary ClassNodes in
888 * our AST. Automatically skips units that have already been processed
889 * through the current phase.
890 */
891 public void applyToPrimaryClassNodes(PrimaryClassNodeOperation body) throws CompilationFailedException {
892 Iterator classNodes = getPrimaryClassNodes(body.needSortedInput()).iterator();
893 while (classNodes.hasNext()) {
894 SourceUnit context=null;
895 try {
896 ClassNode classNode = (ClassNode) classNodes.next();
897 context = classNode.getModule().getContext();
898 if (context == null || context.phase <= phase) {
899 body.call(context, new GeneratorContext(this.ast), classNode);
900 }
901 } catch (CompilationFailedException e) {
902
903 } catch (NullPointerException npe){
904 throw npe;
905 } catch (GroovyBugError e) {
906 changeBugText(e,context);
907 throw e;
908 } catch (Exception e) {
909
910 ErrorCollector nestedCollector = null;
911 for (Throwable next = e.getCause(); next!=e && next!=null; next=next.getCause()) {
912 if (!(next instanceof MultipleCompilationErrorsException)) continue;
913 MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) next;
914 nestedCollector = mcee.collector;
915 break;
916 }
917
918 if (nestedCollector!=null) {
919 getErrorCollector().addCollectorContents(nestedCollector);
920 } else {
921 getErrorCollector().addError(new ExceptionMessage(e,configuration.getDebug(),this));
922 }
923 }
924 }
925
926 getErrorCollector().failIfErrors();
927 }
928
929 public void applyToGeneratedGroovyClasses(GroovyClassOperation body) throws CompilationFailedException {
930 if (this.phase != Phases.OUTPUT && !(this.phase == Phases.CLASS_GENERATION && this.phaseComplete)) {
931 throw new GroovyBugError("CompilationUnit not ready for output(). Current phase="+getPhaseDescription());
932 }
933
934 boolean failures = false;
935
936 Iterator iterator = this.generatedClasses.iterator();
937 while (iterator.hasNext()) {
938
939
940
941 GroovyClass gclass = (GroovyClass) iterator.next();
942 try {
943 body.call(gclass);
944 } catch (CompilationFailedException e) {
945
946 } catch (NullPointerException npe){
947 throw npe;
948 } catch (GroovyBugError e) {
949 changeBugText(e,null);
950 throw e;
951 } catch (Exception e) {
952 GroovyBugError gbe = new GroovyBugError(e);
953 throw gbe;
954 }
955 }
956
957 getErrorCollector().failIfErrors();
958 }
959
960 private void changeBugText(GroovyBugError e, SourceUnit context) {
961 e.setBugText("exception in phase '"+getPhaseDescription()+"' in source unit '"+((context!=null)?context.getName():"?")+"' "+e.getBugText());
962 }
963 }