001 /*
002 $Id: CompilationUnit.java,v 1.34 2005/11/19 21:22:12 blackdrag Exp $
003
004
005 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
006
007
008 Redistribution and use of this software and associated documentation
009 ("Software"), with or without modification, are permitted provided
010 that the following conditions are met:
011
012 1. Redistributions of source code must retain copyright
013 statements and notices. Redistributions must also contain a
014 copy of this document.
015
016
017 2. Redistributions in binary form must reproduce the
018 above copyright notice, this list of conditions and the
019 following disclaimer in the documentation and/or other
020 materials provided with the distribution.
021
022
023 3. The name "groovy" must not be used to endorse or promote
024 products derived from this Software without prior written
025 permission of The Codehaus. For written permission,
026 please contact info@codehaus.org.
027
028
029 4. Products derived from this Software may not be called "groovy"
030 nor may "groovy" appear in their names without prior written
031 permission of The Codehaus. "groovy" is a registered
032 trademark of The Codehaus.
033
034
035 5. Due credit should be given to The Codehaus -
036 http://groovy.codehaus.org/
037
038
039 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
040 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
041 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
042 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
043 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
044 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
045 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
046 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
047 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
048 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
049 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
050 OF THE POSSIBILITY OF SUCH DAMAGE.
051 */
052
053
054 package org.codehaus.groovy.control;
055
056 import java.io.File;
057 import java.io.FileOutputStream;
058 import java.io.IOException;
059 import java.io.InputStream;
060 import java.net.URL;
061 import java.security.CodeSource;
062 import java.util.*;
063
064 import org.codehaus.groovy.GroovyBugError;
065 import org.codehaus.groovy.ast.ASTNode;
066 import org.codehaus.groovy.ast.ClassNode;
067 import org.codehaus.groovy.ast.CompileUnit;
068 import org.codehaus.groovy.ast.ModuleNode;
069 import org.codehaus.groovy.classgen.AsmClassGenerator;
070 import org.codehaus.groovy.classgen.ClassCompletionVerifier;
071 import org.codehaus.groovy.classgen.ClassGenerator;
072 import org.codehaus.groovy.classgen.GeneratorContext;
073 import org.codehaus.groovy.classgen.JSRVariableScopeCodeVisitor;
074 import org.codehaus.groovy.classgen.Verifier;
075 import org.codehaus.groovy.control.io.InputStreamReaderSource;
076 import org.codehaus.groovy.control.io.ReaderSource;
077 import org.codehaus.groovy.control.messages.ExceptionMessage;
078 import org.codehaus.groovy.control.messages.Message;
079 import org.codehaus.groovy.syntax.SyntaxException;
080 import org.codehaus.groovy.syntax.ClassSource;
081 import org.codehaus.groovy.syntax.SourceSummary;
082 import org.codehaus.groovy.tools.GroovyClass;
083 import org.objectweb.asm.ClassVisitor;
084 import org.objectweb.asm.ClassWriter;
085
086 import groovy.lang.GroovyClassLoader;
087 import groovy.lang.GroovyRuntimeException;
088
089 /**
090 * Collects all compilation data as it is generated by the compiler system.
091 * Allows additional source units to be added and compilation run again (to
092 * affect only the deltas).
093 *
094 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
095 * @version $Id: CompilationUnit.java,v 1.34 2005/11/19 21:22:12 blackdrag Exp $
096 */
097
098 public class CompilationUnit extends ProcessingUnit {
099
100
101 //---------------------------------------------------------------------------
102 // CONSTRUCTION AND SUCH
103
104
105 protected HashMap sources; // The SourceUnits from which this unit is built
106 protected Map summariesBySourceName; // Summary of each SourceUnit
107 protected Map summariesByPublicClassName; // Summary of each SourceUnit
108 protected Map classSourcesByPublicClassName; // Summary of each Class
109 protected ArrayList names; // Names for each SourceUnit in sources.
110 protected LinkedList queuedSources;
111
112
113 protected CompileUnit ast; // The overall AST for this CompilationUnit.
114 protected ArrayList generatedClasses; // The classes generated during classgen.
115
116
117 protected Verifier verifier; // For use by verify().
118
119
120 protected ClassCompletionVerifier completionVerifier; // for use by checkClassCompletion
121
122
123 protected boolean debug; // Controls behaviour of classgen() and other routines.
124 protected boolean configured; // Set true after the first configure() operation
125
126
127 protected ClassgenCallback classgenCallback; // A callback for use during classgen()
128 protected ProgressCallback progressCallback; // A callback for use during compile()
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 // SOURCE CREATION
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 // EXTERNAL CALLBACKS
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 // ACTIONS
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 // To support delta compilations, we always restart
448 // the compiler. The individual passes are responsible
449 // for not reprocessing old code.
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 //todo - is this the best way to handle scripts?
563 summariesByPublicClassName.put("*NoName*",sourceSummary);
564 // nothing to put into classSourcesByClassName as no ClassSource
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 // Callback progress, if necessary
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 // Run the Verifier on the outer class
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 // do scope checking
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 // Prep the generator machinery
686 //
687 ClassVisitor visitor = createClassVisitor();
688
689
690 String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName());
691 // only show the file name and its extension like javac does in its stacktraces rather than the full path
692 // also takes care of both \ and / depending on the host compiling environment
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 // Run the generation and create the class (if required)
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 byte[] bytes = ((ClassWriter) visitor).toByteArray();
710 FileOutputStream fos;
711 try {
712 fos = new FileOutputStream(classNode.getType().getName());
713 fos.write(bytes);
714 fos.close();
715 } catch (IOException e) {
716 e.printStackTrace();
717 }*/
718
719
720 //
721 // Handle any callback that's been set
722
723
724 if (CompilationUnit.this.classgenCallback != null) {
725 classgenCallback.call(visitor, classNode);
726 }
727
728
729 //
730 // Recurse for inner classes
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 // Get the class and calculate its filesystem name
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 // Ensure the path is ready for the file
781 //
782 File directory = path.getParentFile();
783 if (directory != null && !directory.exists()) {
784 directory.mkdirs();
785 }
786
787
788 //
789 // Create the file and write out the data
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 // Callback progress, if necessary
820 //
821 if (CompilationUnit.this.progressCallback != null) {
822 CompilationUnit.this.progressCallback.call(this, this.phase);
823 }
824 }
825
826 //---------------------------------------------------------------------------
827 // PHASE HANDLING
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 // LOOP SIMPLIFICATION FOR SourceUnit OPERATIONS
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 // LOOP SIMPLIFICATION FOR PRIMARY ClassNode OPERATIONS
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 // fall thorugh, getErrorREporter().failIfErrors() will triger
978 } catch (NullPointerException npe){
979 throw npe;
980 } catch (Exception e) {
981 failures = true;
982
983 // check the exception for a nested compilation exception
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 }