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

Save This Page
Home » groovy-src-1.6.3 » org.codehaus » groovy » control » [javadoc | source]