001 /* 002 $Id: CompilationUnit.java 4354 2006-12-13 21:14:22Z blackdrag $ 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.VariableScopeVisitor; 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.control.messages.SimpleMessage; 080 import org.codehaus.groovy.syntax.SyntaxException; 081 import org.codehaus.groovy.syntax.ClassSource; 082 import org.codehaus.groovy.syntax.SourceSummary; 083 import org.codehaus.groovy.tools.GroovyClass; 084 import org.objectweb.asm.ClassVisitor; 085 import org.objectweb.asm.ClassWriter; 086 087 import groovy.lang.GroovyClassLoader; 088 import groovy.lang.GroovyRuntimeException; 089 090 /** 091 * Collects all compilation data as it is generated by the compiler system. 092 * Allows additional source units to be added and compilation run again (to 093 * affect only the deltas). 094 * 095 * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a> 096 * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a> 097 * @version $Id: CompilationUnit.java 4354 2006-12-13 21:14:22Z blackdrag $ 098 */ 099 100 public class CompilationUnit extends ProcessingUnit { 101 102 103 //--------------------------------------------------------------------------- 104 // CONSTRUCTION AND SUCH 105 106 107 protected HashMap sources; // The SourceUnits from which this unit is built 108 protected Map summariesBySourceName; // Summary of each SourceUnit 109 protected Map summariesByPublicClassName; // Summary of each SourceUnit 110 protected Map classSourcesByPublicClassName; // Summary of each Class 111 protected ArrayList names; // Names for each SourceUnit in sources. 112 protected LinkedList queuedSources; 113 114 115 protected CompileUnit ast; // The overall AST for this CompilationUnit. 116 protected ArrayList generatedClasses; // The classes generated during classgen. 117 118 119 protected Verifier verifier; // For use by verify(). 120 121 122 protected boolean debug; // Controls behaviour of classgen() and other routines. 123 protected boolean configured; // Set true after the first configure() operation 124 125 126 protected ClassgenCallback classgenCallback; // A callback for use during classgen() 127 protected ProgressCallback progressCallback; // A callback for use during compile() 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 /*for (Iterator iterator = configuration.getClasspath().iterator(); iterator.hasNext(); ) { 233 classLoader.addClasspath((String) iterator.next()); 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 // SOURCE CREATION 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 // EXTERNAL CALLBACKS 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 // ACTIONS 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 // To support delta compilations, we always restart 468 // the compiler. The individual passes are responsible 469 // for not reprocessing old code. 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 // before we actually do the sorting we should check 508 // for cyclic references 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 //todo - is this the best way to handle scripts? 569 summariesByPublicClassName.put("*NoName*",sourceSummary); 570 // nothing to put into classSourcesByClassName as no ClassSource 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 // Ensure the path is ready for the file 624 // 625 File directory = path.getParentFile(); 626 if (directory != null && !directory.exists()) { 627 directory.mkdirs(); 628 } 629 630 // 631 // Create the file and write out the data 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 /* checks if all needed classes are compiled before generating the bytecode */ 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 // Run the Verifier on the outer class 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 // because the class may be generated even if a error was found 726 // and that class may have an invalid format we fail here if needed 727 getErrorCollector().failIfErrors(); 728 729 // 730 // Prep the generator machinery 731 // 732 ClassVisitor visitor = createClassVisitor(); 733 734 735 String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName()); 736 // only show the file name and its extension like javac does in its stacktraces rather than the full path 737 // also takes care of both \ and / depending on the host compiling environment 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 // Run the generation and create the class (if required) 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 // Handle any callback that's been set 755 // 756 if (CompilationUnit.this.classgenCallback != null) { 757 classgenCallback.call(visitor, classNode); 758 } 759 760 761 // 762 // Recurse for inner classes 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 // PHASE HANDLING 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 // LOOP SIMPLIFICATION FOR SourceUnit OPERATIONS 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 // LOOP SIMPLIFICATION FOR PRIMARY ClassNode OPERATIONS 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[] index = new int[unsorted.size()]; 887 { 888 int i = 0; 889 for (Iterator iter = unsorted.iterator(); iter.hasNext(); i++) { 890 ClassNode element = (ClassNode) iter.next(); 891 int count = 0; 892 while (element!=null){ 893 count++; 894 element = element.getSuperClass(); 895 } 896 index[i] = count; 897 } 898 } 899 900 ArrayList sorted = new ArrayList(unsorted.size()); 901 int start = 0; 902 for (int i=0; i<index.length; i++) { 903 int min = -1; 904 for (int j=0; j<index.length; j++) { 905 if (index[j]==-1) continue; 906 if (min==-1) { 907 min = j; 908 } else if (index[j]<index[min]) { 909 min = j; 910 } 911 } 912 sorted.add(unsorted.get(min)); 913 index[min] = -1; 914 } 915 916 return sorted; 917 } 918 919 /** 920 * A loop driver for applying operations to all primary ClassNodes in 921 * our AST. Automatically skips units that have already been processed 922 * through the current phase. 923 */ 924 public void applyToPrimaryClassNodes(PrimaryClassNodeOperation body) throws CompilationFailedException { 925 Iterator classNodes = getPrimaryClassNodes(body.needSortedInput()).iterator(); 926 while (classNodes.hasNext()) { 927 SourceUnit context=null; 928 try { 929 ClassNode classNode = (ClassNode) classNodes.next(); 930 context = classNode.getModule().getContext(); 931 if (context == null || context.phase <= phase) { 932 body.call(context, new GeneratorContext(this.ast), classNode); 933 } 934 } catch (CompilationFailedException e) { 935 // fall thorugh, getErrorREporter().failIfErrors() will triger 936 } catch (NullPointerException npe){ 937 throw npe; 938 } catch (GroovyBugError e) { 939 changeBugText(e,context); 940 throw e; 941 } catch (Exception e) { 942 // check the exception for a nested compilation exception 943 ErrorCollector nestedCollector = null; 944 for (Throwable next = e.getCause(); next!=e && next!=null; next=next.getCause()) { 945 if (!(next instanceof MultipleCompilationErrorsException)) continue; 946 MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) next; 947 nestedCollector = mcee.collector; 948 break; 949 } 950 951 if (nestedCollector!=null) { 952 getErrorCollector().addCollectorContents(nestedCollector); 953 } else { 954 getErrorCollector().addError(new ExceptionMessage(e,configuration.getDebug(),this)); 955 } 956 } 957 } 958 959 getErrorCollector().failIfErrors(); 960 } 961 962 public void applyToGeneratedGroovyClasses(GroovyClassOperation body) throws CompilationFailedException { 963 if (this.phase != Phases.OUTPUT && !(this.phase == Phases.CLASS_GENERATION && this.phaseComplete)) { 964 throw new GroovyBugError("CompilationUnit not ready for output(). Current phase="+getPhaseDescription()); 965 } 966 967 boolean failures = false; 968 969 Iterator iterator = this.generatedClasses.iterator(); 970 while (iterator.hasNext()) { 971 // 972 // Get the class and calculate its filesystem name 973 // 974 GroovyClass gclass = (GroovyClass) iterator.next(); 975 try { 976 body.call(gclass); 977 } catch (CompilationFailedException e) { 978 // fall thorugh, getErrorREporter().failIfErrors() will triger 979 } catch (NullPointerException npe){ 980 throw npe; 981 } catch (GroovyBugError e) { 982 changeBugText(e,null); 983 throw e; 984 } catch (Exception e) { 985 GroovyBugError gbe = new GroovyBugError(e); 986 throw gbe; 987 } 988 } 989 990 getErrorCollector().failIfErrors(); 991 } 992 993 private void changeBugText(GroovyBugError e, SourceUnit context) { 994 e.setBugText("exception in phase '"+getPhaseDescription()+"' in source unit '"+((context!=null)?context.getName():"?")+"' "+e.getBugText()); 995 } 996 }