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