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