You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:26 UTC

[15/62] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
new file mode 100644
index 0000000..dab0e55
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
@@ -0,0 +1,1167 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.control;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyRuntimeException;
+import groovy.transform.CompilationUnitAware;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CompileUnit;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.ClassCompletionVerifier;
+import org.codehaus.groovy.classgen.EnumCompletionVisitor;
+import org.codehaus.groovy.classgen.EnumVisitor;
+import org.codehaus.groovy.classgen.ExtendedVerifier;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.classgen.InnerClassCompletionVisitor;
+import org.codehaus.groovy.classgen.InnerClassVisitor;
+import org.codehaus.groovy.classgen.VariableScopeVisitor;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.control.customizers.CompilationCustomizer;
+import org.codehaus.groovy.control.io.InputStreamReaderSource;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.control.messages.ExceptionMessage;
+import org.codehaus.groovy.control.messages.Message;
+import org.codehaus.groovy.control.messages.SimpleMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.codehaus.groovy.tools.GroovyClass;
+import org.codehaus.groovy.transform.ASTTransformationVisitor;
+import org.codehaus.groovy.transform.AnnotationCollectorTransform;
+import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
+import org.codehaus.groovy.transform.trait.TraitComposer;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.CodeSource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The CompilationUnit collects all compilation data as it is generated by the compiler system.
+ * You can use this object to add additional source units to the compilation, or force the
+ * compilation to be run again (to affect only the deltas).
+ * <p>
+ * You can also add PhaseOperations to this compilation using the addPhaseOperation method.
+ * This is commonly used when you want to wire a new AST Transformation into the compilation.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @author <a href="mailto:roshandawrani@codehaus.org">Roshan Dawrani</a>
+ */
+
+public class CompilationUnit extends ProcessingUnit {
+
+    //---------------------------------------------------------------------------
+    // CONSTRUCTION AND SUCH
+
+    protected ASTTransformationsContext astTransformationsContext; // AST transformations state data
+
+    protected Map<String, SourceUnit> sources;    // The SourceUnits from which this unit is built
+    protected Map summariesBySourceName;      // Summary of each SourceUnit
+    protected Map summariesByPublicClassName;       // Summary of each SourceUnit
+    protected Map classSourcesByPublicClassName;    // Summary of each Class
+    protected List<String> names;      // Names for each SourceUnit in sources.
+    protected LinkedList<SourceUnit> queuedSources;
+
+    protected CompileUnit ast;        // The overall AST for this CompilationUnit.
+    protected List<GroovyClass> generatedClasses;  // The classes generated during classgen.
+
+    protected Verifier verifier;   // For use by verify().
+
+    protected boolean debug;      // Controls behavior of classgen() and other routines.
+    protected boolean configured; // Set true after the first configure() operation
+
+    protected ClassgenCallback classgenCallback;  // A callback for use during classgen()
+    protected ProgressCallback progressCallback;  // A callback for use during compile()
+    protected ResolveVisitor resolveVisitor;
+    protected StaticImportVisitor staticImportVisitor;
+    protected OptimizerVisitor optimizer;
+    protected ClassNodeResolver classNodeResolver;
+
+    LinkedList[] phaseOperations;
+    LinkedList[] newPhaseOperations;
+
+    /**
+     * Initializes the CompilationUnit with defaults.
+     */
+    public CompilationUnit() {
+        this(null, null, null);
+    }
+
+
+    /**
+     * Initializes the CompilationUnit with defaults except for class loader.
+     */
+    public CompilationUnit(GroovyClassLoader loader) {
+        this(null, null, loader);
+    }
+
+
+    /**
+     * Initializes the CompilationUnit with no security considerations.
+     */
+    public CompilationUnit(CompilerConfiguration configuration) {
+        this(configuration, null, null);
+    }
+
+    /**
+     * Initializes the CompilationUnit with a CodeSource for controlling
+     * security stuff and a class loader for loading classes.
+     */
+    public CompilationUnit(CompilerConfiguration configuration, CodeSource security, GroovyClassLoader loader) {
+        this(configuration, security, loader, null);
+    }
+
+    /**
+     * Initializes the CompilationUnit with a CodeSource for controlling
+     * security stuff, a class loader for loading classes, and a class
+     * loader for loading AST transformations.
+     * <b>Note</b> The transform loader must be
+     * able to load compiler classes. That means CompilationUnit.class.classLoader
+     * must be at last a parent to transformLoader. The other loader has no such constraint.
+     *
+     * @param transformLoader - the loader for transforms
+     * @param loader          - loader used to resolve classes against during compilation
+     * @param security        - security setting for the compilation
+     * @param configuration   - compilation configuration
+     */
+    public CompilationUnit(CompilerConfiguration configuration, CodeSource security,
+                           GroovyClassLoader loader, GroovyClassLoader transformLoader) {
+        super(configuration, loader, null);
+
+        this.astTransformationsContext = new ASTTransformationsContext(this, transformLoader);
+        this.names = new ArrayList<String>();
+        this.queuedSources = new LinkedList<SourceUnit>();
+        this.sources = new HashMap<String, SourceUnit>();
+        this.summariesBySourceName = new HashMap();
+        this.summariesByPublicClassName = new HashMap();
+        this.classSourcesByPublicClassName = new HashMap();
+
+        this.ast = new CompileUnit(this.classLoader, security, this.configuration);
+        this.generatedClasses = new ArrayList<GroovyClass>();
+
+        this.verifier = new Verifier();
+        this.resolveVisitor = new ResolveVisitor(this);
+        this.staticImportVisitor = new StaticImportVisitor();
+        this.optimizer = new OptimizerVisitor(this);
+
+        phaseOperations = new LinkedList[Phases.ALL + 1];
+        newPhaseOperations = new LinkedList[Phases.ALL + 1];
+        for (int i = 0; i < phaseOperations.length; i++) {
+            phaseOperations[i] = new LinkedList();
+            newPhaseOperations[i] = new LinkedList();
+        }
+        addPhaseOperation(new SourceUnitOperation() {
+            public void call(SourceUnit source) throws CompilationFailedException {
+                source.parse();
+            }
+        }, Phases.PARSING);
+        addPhaseOperation(convert, Phases.CONVERSION);
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                EnumVisitor ev = new EnumVisitor(CompilationUnit.this, source);
+                ev.visitClass(classNode);
+            }
+        }, Phases.CONVERSION);
+        addPhaseOperation(resolve, Phases.SEMANTIC_ANALYSIS);
+        addPhaseOperation(staticImport, Phases.SEMANTIC_ANALYSIS);
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            @Override
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                InnerClassVisitor iv = new InnerClassVisitor(CompilationUnit.this, source);
+                iv.visitClass(classNode);
+            }
+        }, Phases.SEMANTIC_ANALYSIS);
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            @Override
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                if (!classNode.isSynthetic()) {
+                    GenericsVisitor genericsVisitor = new GenericsVisitor(source);
+                    genericsVisitor.visitClass(classNode);
+                }
+            }
+        }, Phases.SEMANTIC_ANALYSIS);
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                TraitComposer.doExtendTraits(classNode, source, CompilationUnit.this);
+            }
+        }, Phases.CANONICALIZATION);
+        addPhaseOperation(compileCompleteCheck, Phases.CANONICALIZATION);
+        addPhaseOperation(classgen, Phases.CLASS_GENERATION);
+        addPhaseOperation(output);
+
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            @Override
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                AnnotationCollectorTransform.ClassChanger actt = new AnnotationCollectorTransform.ClassChanger();
+                actt.transformClass(classNode);
+            }
+        }, Phases.SEMANTIC_ANALYSIS);
+        ASTTransformationVisitor.addPhaseOperations(this);
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            @Override
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                StaticVerifier sv = new StaticVerifier();
+                sv.visitClass(classNode, source);
+            }
+        }, Phases.SEMANTIC_ANALYSIS);
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            @Override
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                InnerClassCompletionVisitor iv = new InnerClassCompletionVisitor(CompilationUnit.this, source);
+                iv.visitClass(classNode);
+            }
+        }, Phases.CANONICALIZATION);
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                EnumCompletionVisitor ecv = new EnumCompletionVisitor(CompilationUnit.this, source);
+                ecv.visitClass(classNode);
+            }
+        }, Phases.CANONICALIZATION);
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            @Override
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                Object callback = classNode.getNodeMetaData(StaticCompilationMetadataKeys.DYNAMIC_OUTER_NODE_CALLBACK);
+                if (callback instanceof PrimaryClassNodeOperation) {
+                    ((PrimaryClassNodeOperation) callback).call(source, context, classNode);
+                    classNode.removeNodeMetaData(StaticCompilationMetadataKeys.DYNAMIC_OUTER_NODE_CALLBACK);
+                }
+            }
+        }, Phases.INSTRUCTION_SELECTION);
+
+        // apply configuration customizers if any
+        if (configuration != null) {
+            final List<CompilationCustomizer> customizers = configuration.getCompilationCustomizers();
+            for (CompilationCustomizer customizer : customizers) {
+                if (customizer instanceof CompilationUnitAware) {
+                    ((CompilationUnitAware) customizer).setCompilationUnit(this);
+                }
+                addPhaseOperation(customizer, customizer.getPhase().getPhaseNumber());
+            }
+        }
+        this.classgenCallback = null;
+        this.classNodeResolver = new ClassNodeResolver();
+    }
+
+    /**
+     * Returns the class loader for loading AST transformations.
+     * @return - the transform class loader
+     */
+    public GroovyClassLoader getTransformLoader() {
+        return astTransformationsContext.getTransformLoader() == null ? getClassLoader() : astTransformationsContext.getTransformLoader();
+    }
+
+
+    public void addPhaseOperation(SourceUnitOperation op, int phase) {
+        if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown");
+        phaseOperations[phase].add(op);
+    }
+
+    public void addPhaseOperation(PrimaryClassNodeOperation op, int phase) {
+        if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown");
+        phaseOperations[phase].add(op);
+    }
+
+    public void addFirstPhaseOperation(PrimaryClassNodeOperation op, int phase) {
+        if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown");
+        phaseOperations[phase].add(0, op);
+    }
+
+    public void addPhaseOperation(GroovyClassOperation op) {
+        phaseOperations[Phases.OUTPUT].addFirst(op);
+    }
+
+    public void addNewPhaseOperation(SourceUnitOperation op, int phase) {
+        if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown");
+        newPhaseOperations[phase].add(op);
+    }
+
+    /**
+     * Configures its debugging mode and classloader classpath from a given compiler configuration.
+     * This cannot be done more than once due to limitations in {@link java.net.URLClassLoader URLClassLoader}.
+     */
+    public void configure(CompilerConfiguration configuration) {
+        super.configure(configuration);
+        this.debug = configuration.getDebug();
+
+        if (!this.configured && this.classLoader instanceof GroovyClassLoader) {
+            appendCompilerConfigurationClasspathToClassLoader(configuration, (GroovyClassLoader) this.classLoader);
+        }
+
+        this.configured = true;
+    }
+
+    private void appendCompilerConfigurationClasspathToClassLoader(CompilerConfiguration configuration, GroovyClassLoader classLoader) {
+        /*for (Iterator iterator = configuration.getClasspath().iterator(); iterator.hasNext(); ) {
+            classLoader.addClasspath((String) iterator.next());
+        }*/
+    }
+
+    /**
+     * Returns the CompileUnit that roots our AST.
+     */
+    public CompileUnit getAST() {
+        return this.ast;
+    }
+
+    /**
+     * Get the source summaries
+     */
+    public Map getSummariesBySourceName() {
+        return summariesBySourceName;
+    }
+
+    public Map getSummariesByPublicClassName() {
+        return summariesByPublicClassName;
+    }
+
+    public Map getClassSourcesByPublicClassName() {
+        return classSourcesByPublicClassName;
+    }
+
+    public boolean isPublicClass(String className) {
+        return summariesByPublicClassName.containsKey(className);
+    }
+
+    /**
+     * Get the GroovyClasses generated by compile().
+     */
+    public List getClasses() {
+        return generatedClasses;
+    }
+
+    /**
+     * Convenience routine to get the first ClassNode, for
+     * when you are sure there is only one.
+     */
+    public ClassNode getFirstClassNode() {
+        return this.ast.getModules().get(0).getClasses().get(0);
+    }
+
+    /**
+     * Convenience routine to get the named ClassNode.
+     */
+    public ClassNode getClassNode(final String name) {
+        final ClassNode[] result = new ClassNode[]{null};
+        PrimaryClassNodeOperation handler = new PrimaryClassNodeOperation() {
+            public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
+                if (classNode.getName().equals(name)) {
+                    result[0] = classNode;
+                }
+            }
+        };
+
+        try {
+            applyToPrimaryClassNodes(handler);
+        } catch (CompilationFailedException e) {
+            if (debug) e.printStackTrace();
+        }
+        return result[0];
+    }
+
+    /**
+     * @return the AST transformations current context
+     */
+    public ASTTransformationsContext getASTTransformationsContext() {
+        return astTransformationsContext;
+    }
+
+    //---------------------------------------------------------------------------
+    // SOURCE CREATION
+
+
+    /**
+     * Adds a set of file paths to the unit.
+     */
+    public void addSources(String[] paths) {
+        for (String path : paths) {
+            addSource(new File(path));
+        }
+    }
+
+
+    /**
+     * Adds a set of source files to the unit.
+     */
+    public void addSources(File[] files) {
+        for (File file : files) {
+            addSource(file);
+        }
+    }
+
+
+    /**
+     * Adds a source file to the unit.
+     */
+    public SourceUnit addSource(File file) {
+        return addSource(new SourceUnit(file, configuration, classLoader, getErrorCollector()));
+    }
+
+    /**
+     * Adds a source file to the unit.
+     */
+    public SourceUnit addSource(URL url) {
+        return addSource(new SourceUnit(url, configuration, classLoader, getErrorCollector()));
+    }
+
+
+    /**
+     * Adds a InputStream source to the unit.
+     */
+    public SourceUnit addSource(String name, InputStream stream) {
+        ReaderSource source = new InputStreamReaderSource(stream, configuration);
+        return addSource(new SourceUnit(name, source, configuration, classLoader, getErrorCollector()));
+    }
+
+    public SourceUnit addSource(String name, String scriptText) {
+        return addSource(new SourceUnit(name, scriptText, configuration, classLoader, getErrorCollector()));
+    }
+
+    /**
+     * Adds a SourceUnit to the unit.
+     */
+    public SourceUnit addSource(SourceUnit source) {
+        String name = source.getName();
+        source.setClassLoader(this.classLoader);
+        for (SourceUnit su : queuedSources) {
+            if (name.equals(su.getName())) return su;
+        }
+        queuedSources.add(source);
+        return source;
+    }
+
+
+    /**
+     * Returns an iterator on the unit's SourceUnits.
+     */
+    public Iterator<SourceUnit> iterator() {
+        return new Iterator<SourceUnit>() {
+            Iterator<String> nameIterator = names.iterator();
+
+            public boolean hasNext() {
+                return nameIterator.hasNext();
+            }
+
+            public SourceUnit next() {
+                String name = nameIterator.next();
+                return sources.get(name);
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+
+    /**
+     * Adds a ClassNode directly to the unit (ie. without source).
+     * WARNING: the source is needed for error reporting, using
+     * this method without setting a SourceUnit will cause
+     * NullPinterExceptions
+     */
+    public void addClassNode(ClassNode node) {
+        ModuleNode module = new ModuleNode(this.ast);
+        this.ast.addModule(module);
+        module.addClass(node);
+    }
+
+    //---------------------------------------------------------------------------
+    // EXTERNAL CALLBACKS
+
+
+    /**
+     * A callback interface you can use to "accompany" the classgen()
+     * code as it traverses the ClassNode tree.  You will be called-back
+     * for each primary and inner class.  Use setClassgenCallback() before
+     * running compile() to set your callback.
+     */
+    public abstract static class ClassgenCallback {
+        public abstract void call(ClassVisitor writer, ClassNode node) throws CompilationFailedException;
+    }
+
+    /**
+     * Sets a ClassgenCallback.  You can have only one, and setting
+     * it to null removes any existing setting.
+     */
+    public void setClassgenCallback(ClassgenCallback visitor) {
+        this.classgenCallback = visitor;
+    }
+
+    /**
+     * A callback interface you can use to get a callback after every
+     * unit of the compile process.  You will be called-back with a
+     * ProcessingUnit and a phase indicator.  Use setProgressCallback()
+     * before running compile() to set your callback.
+     */
+    public abstract static class ProgressCallback {
+
+        public abstract void call(ProcessingUnit context, int phase) throws CompilationFailedException;
+    }
+
+    /**
+     * Sets a ProgressCallback.  You can have only one, and setting
+     * it to null removes any existing setting.
+     */
+    public void setProgressCallback(ProgressCallback callback) {
+        this.progressCallback = callback;
+    }
+
+    public ClassgenCallback getClassgenCallback() {
+        return classgenCallback;
+    }
+
+    public ProgressCallback getProgressCallback() {
+        return progressCallback;
+    }
+
+    //---------------------------------------------------------------------------
+    // ACTIONS
+
+
+    /**
+     * Synonym for compile(Phases.ALL).
+     */
+    public void compile() throws CompilationFailedException {
+        compile(Phases.ALL);
+    }
+
+    /**
+     * Compiles the compilation unit from sources.
+     */
+    public void compile(int throughPhase) throws CompilationFailedException {
+        //
+        // To support delta compilations, we always restart
+        // the compiler.  The individual passes are responsible
+        // for not reprocessing old code.
+        gotoPhase(Phases.INITIALIZATION);
+        throughPhase = Math.min(throughPhase, Phases.ALL);
+
+        while (throughPhase >= phase && phase <= Phases.ALL) {
+
+            if (phase == Phases.SEMANTIC_ANALYSIS) {
+                doPhaseOperation(resolve);
+                if (dequeued()) continue;
+            }
+
+            processPhaseOperations(phase);
+            // Grab processing may have brought in new AST transforms into various phases, process them as well
+            processNewPhaseOperations(phase);
+
+            if (progressCallback != null) progressCallback.call(this, phase);
+            completePhase();
+            applyToSourceUnits(mark);
+
+            if (dequeued()) continue;
+
+            gotoPhase(phase + 1);
+
+            if (phase == Phases.CLASS_GENERATION) {
+                sortClasses();
+            }
+        }
+
+        errorCollector.failIfErrors();
+    }
+
+    private void processPhaseOperations(int ph) {
+        LinkedList ops = phaseOperations[ph];
+        for (Object next : ops) {
+            doPhaseOperation(next);
+        }
+    }
+
+    private void processNewPhaseOperations(int currPhase) {
+        recordPhaseOpsInAllOtherPhases(currPhase);
+        LinkedList currentPhaseNewOps = newPhaseOperations[currPhase];
+        while (!currentPhaseNewOps.isEmpty()) {
+            Object operation = currentPhaseNewOps.removeFirst();
+            // push this operation to master list and then process it.
+            phaseOperations[currPhase].add(operation);
+            doPhaseOperation(operation);
+            // if this operation has brought in more phase ops for ast transforms, keep recording them
+            // in master list of other phases and keep processing them for this phase.
+            recordPhaseOpsInAllOtherPhases(currPhase);
+            currentPhaseNewOps = newPhaseOperations[currPhase];
+        }
+
+    }
+
+    private void doPhaseOperation(Object operation) {
+        if (operation instanceof PrimaryClassNodeOperation) {
+            applyToPrimaryClassNodes((PrimaryClassNodeOperation) operation);
+        } else if (operation instanceof SourceUnitOperation) {
+            applyToSourceUnits((SourceUnitOperation) operation);
+        } else {
+            applyToGeneratedGroovyClasses((GroovyClassOperation) operation);
+        }
+    }
+
+    private void recordPhaseOpsInAllOtherPhases(int currPhase) {
+        // apart from current phase, push new operations for every other phase in the master phase ops list
+        for (int ph = Phases.INITIALIZATION; ph <= Phases.ALL; ph++) {
+            if (ph != currPhase && !newPhaseOperations[ph].isEmpty()) {
+                phaseOperations[ph].addAll(newPhaseOperations[ph]);
+                newPhaseOperations[ph].clear();
+            }
+        }
+    }
+
+    private void sortClasses() throws CompilationFailedException {
+        for (ModuleNode module : this.ast.getModules()) {
+            module.sortClasses();
+        }
+    }
+
+
+    /**
+     * Dequeues any source units add through addSource and resets the compiler phase
+     * to initialization.
+     * <p>
+     * Note: this does not mean a file is recompiled. If a SourceUnit has already passed
+     * a phase it is skipped until a higher phase is reached.
+     *
+     * @return true if there was a queued source
+     * @throws CompilationFailedException
+     */
+    protected boolean dequeued() throws CompilationFailedException {
+        boolean dequeue = !queuedSources.isEmpty();
+        while (!queuedSources.isEmpty()) {
+            SourceUnit su = queuedSources.removeFirst();
+            String name = su.getName();
+            names.add(name);
+            sources.put(name, su);
+        }
+        if (dequeue) {
+            gotoPhase(Phases.INITIALIZATION);
+        }
+        return dequeue;
+    }
+
+    /**
+     * Resolves all types
+     */
+    private final SourceUnitOperation resolve = new SourceUnitOperation() {
+        public void call(SourceUnit source) throws CompilationFailedException {
+            List<ClassNode> classes = source.ast.getClasses();
+            for (ClassNode node : classes) {
+                VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source);
+                scopeVisitor.visitClass(node);
+
+                resolveVisitor.setClassNodeResolver(classNodeResolver);
+                resolveVisitor.startResolving(node, source);
+            }
+
+        }
+    };
+
+    private final PrimaryClassNodeOperation staticImport = new PrimaryClassNodeOperation() {
+        public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
+            staticImportVisitor.visitClass(classNode, source);
+        }
+    };
+
+    /**
+     * Runs convert() on a single SourceUnit.
+     */
+    private final SourceUnitOperation convert = new SourceUnitOperation() {
+        public void call(SourceUnit source) throws CompilationFailedException {
+            source.convert();
+            CompilationUnit.this.ast.addModule(source.getAST());
+
+
+            if (CompilationUnit.this.progressCallback != null) {
+                CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase);
+            }
+        }
+    };
+
+    private final GroovyClassOperation output = new GroovyClassOperation() {
+        public void call(GroovyClass gclass) throws CompilationFailedException {
+            String name = gclass.getName().replace('.', File.separatorChar) + ".class";
+            File path = new File(configuration.getTargetDirectory(), name);
+
+            //
+            // Ensure the path is ready for the file
+            //
+            File directory = path.getParentFile();
+            if (directory != null && !directory.exists()) {
+                directory.mkdirs();
+            }
+
+            //
+            // Create the file and write out the data
+            //
+            byte[] bytes = gclass.getBytes();
+
+            FileOutputStream stream = null;
+            try {
+                stream = new FileOutputStream(path);
+                stream.write(bytes, 0, bytes.length);
+            } catch (IOException e) {
+                getErrorCollector().addError(Message.create(e.getMessage(), CompilationUnit.this));
+            } finally {
+                if (stream != null) {
+                    try {
+                        stream.close();
+                    } catch (Exception e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+    };
+
+    /* checks if all needed classes are compiled before generating the bytecode */
+    private final SourceUnitOperation compileCompleteCheck = new SourceUnitOperation() {
+        public void call(SourceUnit source) throws CompilationFailedException {
+            List<ClassNode> classes = source.ast.getClasses();
+            for (ClassNode node : classes) {
+                CompileUnit cu = node.getCompileUnit();
+                for (Iterator iter = cu.iterateClassNodeToCompile(); iter.hasNext();) {
+                    String name = (String) iter.next();
+                    SourceUnit su = ast.getScriptSourceLocation(name);
+                    List<ClassNode> classesInSourceUnit = su.ast.getClasses();
+                    StringBuilder message = new StringBuilder();
+                    message
+                            .append("Compilation incomplete: expected to find the class ")
+                            .append(name)
+                            .append(" in ")
+                            .append(su.getName());
+                    if (classesInSourceUnit.isEmpty()) {
+                        message.append(", but the file seems not to contain any classes");
+                    } else {
+                        message.append(", but the file contains the classes: ");
+                        boolean first = true;
+                        for (ClassNode cn : classesInSourceUnit) {
+                            if (!first) {
+                                message.append(", ");
+                            } else {
+                                first = false;
+                            }
+                            message.append(cn.getName());
+                        }
+                    }
+
+                    getErrorCollector().addErrorAndContinue(
+                            new SimpleMessage(message.toString(), CompilationUnit.this)
+                    );
+                    iter.remove();
+                }
+            }
+        }
+    };
+
+
+    /**
+     * Runs classgen() on a single ClassNode.
+     */
+    private final PrimaryClassNodeOperation classgen = new PrimaryClassNodeOperation() {
+        public boolean needSortedInput() {
+            return true;
+        }
+
+        public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
+
+            optimizer.visitClass(classNode, source); // GROOVY-4272: repositioned it here from staticImport
+
+            //
+            // Run the Verifier on the outer class
+            //
+            try {
+                verifier.visitClass(classNode);
+            } catch (GroovyRuntimeException rpe) {
+                ASTNode node = rpe.getNode();
+                getErrorCollector().addError(
+                        new SyntaxException(rpe.getMessage(), node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber()),
+                        source
+                );
+            }
+
+            LabelVerifier lv = new LabelVerifier(source);
+            lv.visitClass(classNode);
+
+            ClassCompletionVerifier completionVerifier = new ClassCompletionVerifier(source);
+            completionVerifier.visitClass(classNode);
+
+            ExtendedVerifier xverifier = new ExtendedVerifier(source);
+            xverifier.visitClass(classNode);
+
+            // because the class may be generated even if a error was found
+            // and that class may have an invalid format we fail here if needed
+            getErrorCollector().failIfErrors();
+
+            //
+            // Prep the generator machinery
+            //
+            ClassVisitor visitor = createClassVisitor();
+            
+            String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName());
+            // only show the file name and its extension like javac does in its stacktraces rather than the full path
+            // also takes care of both \ and / depending on the host compiling environment
+            if (sourceName != null)
+                sourceName = sourceName.substring(Math.max(sourceName.lastIndexOf('\\'), sourceName.lastIndexOf('/')) + 1);
+            AsmClassGenerator generator = new AsmClassGenerator(source, context, visitor, sourceName);
+
+            //
+            // Run the generation and create the class (if required)
+            //
+            generator.visitClass(classNode);
+
+            byte[] bytes = ((ClassWriter) visitor).toByteArray();
+            generatedClasses.add(new GroovyClass(classNode.getName(), bytes));
+
+            //
+            // Handle any callback that's been set
+            //
+            if (CompilationUnit.this.classgenCallback != null) {
+                classgenCallback.call(visitor, classNode);
+            }
+
+            //
+            // Recurse for inner classes
+            //
+            LinkedList innerClasses = generator.getInnerClasses();
+            while (!innerClasses.isEmpty()) {
+                classgen.call(source, context, (ClassNode) innerClasses.removeFirst());
+            }
+        }
+    };
+
+    protected ClassVisitor createClassVisitor() {
+        CompilerConfiguration config = getConfiguration();
+        int computeMaxStackAndFrames = ClassWriter.COMPUTE_MAXS;
+        if (CompilerConfiguration.isPostJDK7(config.getTargetBytecode())
+                || Boolean.TRUE.equals(config.getOptimizationOptions().get("indy"))) {
+            computeMaxStackAndFrames += ClassWriter.COMPUTE_FRAMES;
+        }
+        return new ClassWriter(computeMaxStackAndFrames) {
+            private ClassNode getClassNode(String name) {
+                // try classes under compilation
+                CompileUnit cu = getAST();
+                ClassNode cn = cu.getClass(name);
+                if (cn!=null) return cn;
+                // try inner classes
+                cn = cu.getGeneratedInnerClass(name);
+                if (cn!=null) return cn;
+                // try class loader classes
+                try {
+                    cn = ClassHelper.make(
+                            cu.getClassLoader().loadClass(name,false,true),
+                            false);
+                } catch (Exception e) {
+                    throw new GroovyBugError(e);
+                }
+                return cn;
+            }
+            private ClassNode getCommonSuperClassNode(ClassNode c, ClassNode d) {
+                // adapted from ClassWriter code
+                if (c.isDerivedFrom(d)) return d;
+                if (d.isDerivedFrom(c)) return c;
+                if (c.isInterface() || d.isInterface()) return ClassHelper.OBJECT_TYPE;
+                do {
+                    c = c.getSuperClass();
+                } while (c!=null && !d.isDerivedFrom(c));
+                if (c==null) return ClassHelper.OBJECT_TYPE;
+                return c;
+            }
+            @Override
+            protected String getCommonSuperClass(String arg1, String arg2) {
+                ClassNode a = getClassNode(arg1.replace('/', '.')); 
+                ClassNode b = getClassNode(arg2.replace('/', '.'));
+                return getCommonSuperClassNode(a,b).getName().replace('.','/');
+            }
+
+        };
+    }
+    
+    //---------------------------------------------------------------------------
+    // PHASE HANDLING
+
+    /**
+     * Updates the phase marker on all sources.
+     */
+    protected void mark() throws CompilationFailedException {
+        applyToSourceUnits(mark);
+    }
+
+
+    /**
+     * Marks a single SourceUnit with the current phase,
+     * if it isn't already there yet.
+     */
+    private final SourceUnitOperation mark = new SourceUnitOperation() {
+        public void call(SourceUnit source) throws CompilationFailedException {
+            if (source.phase < phase) {
+                source.gotoPhase(phase);
+            }
+
+            if (source.phase == phase && phaseComplete && !source.phaseComplete) {
+                source.completePhase();
+            }
+        }
+    };
+
+    //---------------------------------------------------------------------------
+    // LOOP SIMPLIFICATION FOR SourceUnit OPERATIONS
+
+
+    /**
+     * An callback interface for use in the applyToSourceUnits loop driver.
+     */
+    public abstract static class SourceUnitOperation {
+        public abstract void call(SourceUnit source) throws CompilationFailedException;
+    }
+
+
+    /**
+     * A loop driver for applying operations to all SourceUnits.
+     * Automatically skips units that have already been processed
+     * through the current phase.
+     */
+    public void applyToSourceUnits(SourceUnitOperation body) throws CompilationFailedException {
+        for (String name : names) {
+            SourceUnit source = sources.get(name);
+            if ((source.phase < phase) || (source.phase == phase && !source.phaseComplete)) {
+                try {
+                    body.call(source);
+                } catch (CompilationFailedException e) {
+                    throw e;
+                } catch (Exception e) {
+                    GroovyBugError gbe = new GroovyBugError(e);
+                    changeBugText(gbe, source);
+                    throw gbe;
+                } catch (GroovyBugError e) {
+                    changeBugText(e, source);
+                    throw e;
+                }
+            }
+        }
+
+
+        getErrorCollector().failIfErrors();
+    }
+
+    //---------------------------------------------------------------------------
+    // LOOP SIMPLIFICATION FOR PRIMARY ClassNode OPERATIONS
+
+
+    /**
+     * An callback interface for use in the applyToPrimaryClassNodes loop driver.
+     */
+    public abstract static class PrimaryClassNodeOperation {
+        public abstract void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException;
+
+        public boolean needSortedInput() {
+            return false;
+        }
+    }
+
+    public abstract static class GroovyClassOperation {
+        public abstract void call(GroovyClass gclass) throws CompilationFailedException;
+    }
+
+    private static int getSuperClassCount(ClassNode element) {
+        int count = 0;
+        while (element != null) {
+            count++;
+            element = element.getSuperClass();
+        }
+        return count;
+    }
+
+    private int getSuperInterfaceCount(ClassNode element) {
+        int count = 1;
+        ClassNode[] interfaces = element.getInterfaces();
+        for (ClassNode anInterface : interfaces) {
+            count = Math.max(count, getSuperInterfaceCount(anInterface) + 1);
+        }
+        return count;
+    }
+
+    private List<ClassNode> getPrimaryClassNodes(boolean sort) {
+        List<ClassNode> unsorted = new ArrayList<ClassNode>();
+        for (ModuleNode module : this.ast.getModules()) {
+            for (ClassNode classNode : module.getClasses()) {
+                unsorted.add(classNode);
+            }
+        }
+
+        if (!sort) return unsorted;
+
+        int unsortedSize = unsorted.size();
+        int[] indexClass = new int[unsortedSize];
+        int[] indexInterface = new int[unsortedSize];
+        {
+            int i = 0;
+            for (Iterator<ClassNode> iter = unsorted.iterator(); iter.hasNext(); i++) {
+                ClassNode element = iter.next();
+                if (element.isInterface()) {
+                    indexInterface[i] = getSuperInterfaceCount(element);
+                    indexClass[i] = -1;
+                } else {
+                    indexClass[i] = getSuperClassCount(element);
+                    indexInterface[i] = -1;
+                }
+            }
+        }
+
+        List<ClassNode> sorted = getSorted(indexInterface, unsorted);
+        sorted.addAll(getSorted(indexClass, unsorted));
+        return sorted;
+    }
+
+    private static List<ClassNode> getSorted(int[] index, List<ClassNode> unsorted) {
+        int unsortedSize = unsorted.size();
+        List<ClassNode> sorted = new ArrayList<ClassNode>(unsortedSize);
+        for (int i = 0; i < unsortedSize; i++) {
+            int min = -1;
+            for (int j = 0; j < unsortedSize; j++) {
+                if (index[j] == -1) continue;
+                if (min == -1 || index[j] < index[min]) {
+                    min = j;
+                }
+            }
+            if (min == -1) break;
+            sorted.add(unsorted.get(min));
+            index[min] = -1;
+        }
+        return sorted;
+    }
+
+    /**
+     * A loop driver for applying operations to all primary ClassNodes in
+     * our AST.  Automatically skips units that have already been processed
+     * through the current phase.
+     */
+    public void applyToPrimaryClassNodes(PrimaryClassNodeOperation body) throws CompilationFailedException {
+        for (ClassNode classNode : getPrimaryClassNodes(body.needSortedInput())) {
+            SourceUnit context = null;
+            try {
+                context = classNode.getModule().getContext();
+                if (context == null || context.phase < phase || (context.phase == phase && !context.phaseComplete)) {
+                    int offset = 1;
+                    for (Iterator<InnerClassNode> iterator = classNode.getInnerClasses(); iterator.hasNext(); ) {
+                        iterator.next();
+                        offset++;
+                    }
+                    body.call(context, new GeneratorContext(this.ast, offset), classNode);
+                }
+            } catch (CompilationFailedException e) {
+                // fall through, getErrorReporter().failIfErrors() will trigger
+            } catch (NullPointerException npe) {
+                GroovyBugError gbe = new GroovyBugError("unexpected NullpointerException", npe);
+                changeBugText(gbe, context);
+                throw gbe;
+            } catch (GroovyBugError e) {
+                changeBugText(e, context);
+                throw e;
+            } catch (NoClassDefFoundError e) {
+                // effort to get more logging in case a dependency of a class is loaded
+                // although it shouldn't have
+                convertUncaughtExceptionToCompilationError(e);
+            } catch (Exception e) {
+                convertUncaughtExceptionToCompilationError(e);
+            }
+        }
+
+        getErrorCollector().failIfErrors();
+    }
+
+    private void convertUncaughtExceptionToCompilationError(final Throwable e) {
+        // check the exception for a nested compilation exception
+        ErrorCollector nestedCollector = null;
+        for (Throwable next = e.getCause(); next != e && next != null; next = next.getCause()) {
+            if (!(next instanceof MultipleCompilationErrorsException)) continue;
+            MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) next;
+            nestedCollector = mcee.collector;
+            break;
+        }
+
+        if (nestedCollector != null) {
+            getErrorCollector().addCollectorContents(nestedCollector);
+        } else {
+            Exception err = e instanceof Exception?((Exception)e):new RuntimeException(e);
+            getErrorCollector().addError(new ExceptionMessage(err, configuration.getDebug(), this));
+        }
+    }
+
+    public void applyToGeneratedGroovyClasses(GroovyClassOperation body) throws CompilationFailedException {
+        if (this.phase != Phases.OUTPUT && !(this.phase == Phases.CLASS_GENERATION && this.phaseComplete)) {
+            throw new GroovyBugError("CompilationUnit not ready for output(). Current phase=" + getPhaseDescription());
+        }
+
+        for (GroovyClass gclass : this.generatedClasses) {
+            //
+            // Get the class and calculate its filesystem name
+            //
+            try {
+                body.call(gclass);
+            } catch (CompilationFailedException e) {
+                // fall through, getErrorReporter().failIfErrors() will trigger
+            } catch (NullPointerException npe) {
+                throw npe;
+            } catch (GroovyBugError e) {
+                changeBugText(e, null);
+                throw e;
+            } catch (Exception e) {
+                throw new GroovyBugError(e);
+            }
+        }
+
+        getErrorCollector().failIfErrors();
+    }
+
+    private void changeBugText(GroovyBugError e, SourceUnit context) {
+        e.setBugText("exception in phase '" + getPhaseDescription() + "' in source unit '" + ((context != null) ? context.getName() : "?") + "' " + e.getBugText());
+    }
+    
+    public ClassNodeResolver getClassNodeResolver() {
+        return classNodeResolver;
+    }
+
+
+    public void setClassNodeResolver(ClassNodeResolver classNodeResolver) {
+        this.classNodeResolver = classNodeResolver;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/CompilePhase.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/CompilePhase.java b/src/main/java/org/codehaus/groovy/control/CompilePhase.java
new file mode 100644
index 0000000..7b25fed
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/CompilePhase.java
@@ -0,0 +1,118 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.control;
+
+/**
+* The phases of the GroovyCompiler. This is an enum facade on top of the 
+* Phases object. In general, prefer using this object over Phases. 
+*
+* @author Hamlet D'Arcy
+*/ 
+public enum CompilePhase {
+
+    /**
+    * source files are opened and environment configured
+    */ 
+    INITIALIZATION(Phases.INITIALIZATION),
+    
+    /**
+    * the grammar is used to to produce tree of tokens representing the source code
+    */ 
+    PARSING(Phases.PARSING),
+    
+    /**
+    * An abstract syntax tree (AST) is created from token trees
+    */ 
+    CONVERSION(Phases.CONVERSION),
+    
+    /**
+    * Performs consistency and validity checks that the grammar can't check for, and resolves classes
+    */ 
+    SEMANTIC_ANALYSIS(Phases.SEMANTIC_ANALYSIS),
+    
+    /**
+    * Complete building the AST
+    */ 
+    CANONICALIZATION(Phases.CANONICALIZATION),
+    
+    /**
+    * instruction set is chosen, for example java5 or pre java5
+    */ 
+    INSTRUCTION_SELECTION(Phases.INSTRUCTION_SELECTION),
+    
+    /**
+    * creates the binary output in memory
+    */ 
+    CLASS_GENERATION(Phases.CLASS_GENERATION),
+    
+    /**
+    * write the binary output to the file system
+    */ 
+    OUTPUT(Phases.OUTPUT),
+    
+    /**
+    * Perform any last cleanup
+    */ 
+    FINALIZATION(Phases.FINALIZATION),
+    ;
+
+    /**
+    * The phases as an array, with a null entry. 
+    */ 
+    public static CompilePhase[] phases = {
+        null,
+        INITIALIZATION,
+        PARSING,
+        CONVERSION,
+        SEMANTIC_ANALYSIS,
+        CANONICALIZATION,
+        INSTRUCTION_SELECTION,
+        CLASS_GENERATION,
+        OUTPUT,
+        FINALIZATION,
+    };
+
+    int phaseNumber;
+    CompilePhase(int phaseNumber) {
+        this.phaseNumber = phaseNumber;
+    }
+
+    /**
+    * Returns the underlieng integer Phase number. 
+    */ 
+    public int getPhaseNumber() {
+        return phaseNumber;
+    }
+
+    /**
+     * Returns the CompilePhase for the given integer phase number.
+     * @param phaseNumber
+     *      the phase number
+     * @return
+     *      the CompilePhase or null if not found
+     */
+    public static CompilePhase fromPhaseNumber(int phaseNumber) {
+        for (CompilePhase phase : values()) {
+            if (phase.phaseNumber == phaseNumber) {
+                return phase;
+            }
+        }
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java b/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
new file mode 100644
index 0000000..9f6babe
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/CompilerConfiguration.java
@@ -0,0 +1,934 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.control;
+
+import org.apache.groovy.util.Maps;
+import org.codehaus.groovy.control.customizers.CompilationCustomizer;
+import org.codehaus.groovy.control.io.NullWriter;
+import org.codehaus.groovy.control.messages.WarningMessage;
+import org.objectweb.asm.Opcodes;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * Compilation control flags and coordination stuff.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @author <a href="mailto:jim@pagesmiths.com">Jim White</a>
+ * @author <a href="mailto:cedric.champeau@gmail.com">Cedric Champeau</a>
+ */
+
+public class CompilerConfiguration {
+
+    /** This (<code>"indy"</code>) is the Optimization Option value for enabling <code>invokedynamic</code> complilation. */
+    public static final String INVOKEDYNAMIC = "indy";
+
+    /** This (<code>"1.4"</code>) is the value for targetBytecode to compile for a JDK 1.4. **/
+    public static final String JDK4 = "1.4";
+    /** This (<code>"1.5"</code>) is the value for targetBytecode to compile for a JDK 1.5. **/
+    public static final String JDK5 = "1.5";
+    /** This (<code>"1.6"</code>) is the value for targetBytecode to compile for a JDK 1.6. **/
+    public static final String JDK6 = "1.6";
+    /** This (<code>"1.7"</code>) is the value for targetBytecode to compile for a JDK 1.7. **/
+    public static final String JDK7 = "1.7";
+    /** This (<code>"1.8"</code>) is the value for targetBytecode to compile for a JDK 1.8. **/
+    public static final String JDK8 = "1.8";
+
+    /** This (<code>"1.5"</code>) is the value for targetBytecode to compile for a JDK 1.5 or later JVM. **/
+    public static final String POST_JDK5 = JDK5; // for backwards compatibility
+
+    /** This (<code>"1.4"</code>) is the value for targetBytecode to compile for a JDK 1.4 JVM. **/
+    public static final String PRE_JDK5 = JDK4;
+
+    /**
+     * JDK version to bytecode version mapping
+     */
+    public static final Map<String, Integer> JDK_TO_BYTECODE_VERSION_MAP = Maps.of(
+            JDK4, Opcodes.V1_4,
+            JDK5, Opcodes.V1_5,
+            JDK6, Opcodes.V1_6,
+            JDK7, Opcodes.V1_7,
+            JDK8, Opcodes.V1_8
+    );
+
+    /** An array of the valid targetBytecode values **/
+    public static final String[] ALLOWED_JDKS = JDK_TO_BYTECODE_VERSION_MAP.keySet().toArray(new String[0]);
+
+    // Just call getVMVersion() once.
+    public static final String CURRENT_JVM_VERSION = getVMVersion();
+
+    private static final String GROOVY_ANTLR4_OPT = "groovy.antlr4";
+
+    /**
+     * The default source encoding
+     */
+    public static final String DEFAULT_SOURCE_ENCODING = "UTF-8";
+
+    // Static initializers are executed in text order,
+    // therefore we must do this one last!
+    /**
+     *  A convenience for getting a default configuration.  Do not modify it!
+     *  See {@link #CompilerConfiguration(Properties)} for an example on how to
+     *  make a suitable copy to modify.  But if you're really starting from a
+     *  default context, then you probably just want <code>new CompilerConfiguration()</code>. 
+     */
+    public static final CompilerConfiguration DEFAULT = new CompilerConfiguration();
+
+    /**
+     * See {@link WarningMessage} for levels.
+     */
+    private int warningLevel;
+
+    /**
+     * Encoding for source files
+     */
+    private String sourceEncoding;
+
+    /**
+     * The <code>PrintWriter</code> does nothing.
+     */
+    private PrintWriter output;
+
+    /**
+     * Directory into which to write classes
+     */
+    private File targetDirectory;
+
+    /**
+     * Classpath for use during compilation
+     */
+    private LinkedList<String> classpath;
+
+    /**
+     * If true, the compiler should produce action information
+     */
+    private boolean verbose;
+
+    /**
+     * If true, debugging code should be activated
+     */
+    private boolean debug;
+
+    /**
+     * If true, generates metadata for reflection on method parameters
+     */
+    private boolean parameters = false;
+
+    /**
+     * The number of non-fatal errors to allow before bailing
+     */
+    private int tolerance;
+
+    /**
+     * Base class name for scripts (must derive from Script)
+     */
+    private String scriptBaseClass;
+
+    private ParserPluginFactory pluginFactory;
+
+    /**
+     * extension used to find a groovy file
+     */
+    private String defaultScriptExtension;
+
+    /**
+     * extensions used to find a groovy files
+     */
+    private Set<String> scriptExtensions = new LinkedHashSet<String>();
+
+    /**
+     * if set to true recompilation is enabled
+     */
+    private boolean recompileGroovySource;
+
+    /**
+     * sets the minimum of time after a script can be recompiled.
+     */
+    private int minimumRecompilationInterval;
+
+    /**
+     * sets the bytecode version target
+     */
+    private String targetBytecode;
+
+    /**
+     * options for joint compilation (null by default == no joint compilation)
+     */
+    private Map<String, Object> jointCompilationOptions;
+
+    /**
+     * options for optimizations (empty map by default)
+     */
+    private Map<String, Boolean> optimizationOptions;
+
+    private final List<CompilationCustomizer> compilationCustomizers = new LinkedList<CompilationCustomizer>();
+
+    /**
+     * Sets a list of global AST transformations which should not be loaded even if they are
+     * defined in META-INF/org.codehaus.groovy.transform.ASTTransformation files. By default,
+     * none is disabled.
+     */
+    private Set<String> disabledGlobalASTTransformations;
+
+    private BytecodeProcessor bytecodePostprocessor;
+
+    /**
+     * defines if antlr2 parser should be used or the antlr4 one if
+     * no factory is set yet
+     */
+    private ParserVersion parserVersion = ParserVersion.V_2;
+
+    /**
+     * Sets the Flags to defaults.
+     */
+    public CompilerConfiguration() {
+        //
+        // Set in safe defaults
+
+        setWarningLevel(WarningMessage.LIKELY_ERRORS);
+        setOutput(null);
+        setTargetDirectory((File) null);
+        setClasspath("");
+        setVerbose(false);
+        setDebug(false);
+        setParameters(safeGetSystemProperty("groovy.parameters") != null);
+        setTolerance(10);
+        setScriptBaseClass(null);
+        setRecompileGroovySource(false);
+        setMinimumRecompilationInterval(100);
+        setTargetBytecode(safeGetSystemProperty("groovy.target.bytecode", getVMVersion()));
+        setDefaultScriptExtension(safeGetSystemProperty("groovy.default.scriptExtension", ".groovy"));
+
+        // Source file encoding
+        String encoding = safeGetSystemProperty("file.encoding", DEFAULT_SOURCE_ENCODING);
+        encoding = safeGetSystemProperty("groovy.source.encoding", encoding);
+        setSourceEncoding(encoding);
+
+        try {
+            setOutput(new PrintWriter(System.err));
+        } catch (Exception e) {
+            // IGNORE
+        }
+
+
+        String target = safeGetSystemProperty("groovy.target.directory");
+        if (target != null) {
+            setTargetDirectory(target);
+        }
+
+        boolean indy = false;
+        try {
+            indy = Boolean.getBoolean("groovy.target.indy");
+        } catch (Exception e) {
+            // IGNORE
+        }
+        if (DEFAULT!=null && Boolean.TRUE.equals(DEFAULT.getOptimizationOptions().get(INVOKEDYNAMIC))) {
+            indy = true;
+        }
+        Map options = new HashMap<String,Boolean>(3);
+        if (indy) {
+            options.put(INVOKEDYNAMIC, Boolean.TRUE);
+        }
+        setOptimizationOptions(options);
+
+        try {
+            String groovyAntlr4Opt = System.getProperty(GROOVY_ANTLR4_OPT);
+
+            this.parserVersion = !Boolean.valueOf(groovyAntlr4Opt)
+                            ? ParserVersion.V_2
+                            : ParserVersion.V_4;
+        } catch (Exception e) {
+            // IGNORE
+        }
+    }
+
+    /**
+     * Retrieves a System property, or null if any of the following exceptions occur.
+     * <ul>
+     *     <li>SecurityException - if a security manager exists and its checkPropertyAccess method doesn't allow access to the specified system property.</li>
+     *     <li>NullPointerException - if key is null.</li>
+     *     <li>IllegalArgumentException - if key is empty.</li>
+     * </ul>
+     * @param key the name of the system property.
+     * @return value of the system property or null
+     */
+    private static String safeGetSystemProperty(String key){
+        return safeGetSystemProperty(key, null);
+    }
+
+    /**
+     * Retrieves a System property, or null if any of the following exceptions occur (Warning: Exception messages are
+     * suppressed).
+     * <ul>
+     *     <li>SecurityException - if a security manager exists and its checkPropertyAccess method doesn't allow access to the specified system property.</li>
+     *     <li>NullPointerException - if key is null.</li>
+     *     <li>IllegalArgumentException - if key is empty.</li>
+     * </ul>
+     * @param key the name of the system property.
+     * @param def a default value.
+     * @return  value of the system property or null
+     */
+    private static String safeGetSystemProperty(String key, String def){
+        try {
+            return System.getProperty(key, def);
+        } catch (SecurityException t){
+            // suppress exception
+        } catch (NullPointerException t){
+            // suppress exception
+        } catch (IllegalArgumentException t){
+            // suppress exception
+        }
+        return def;
+    }
+
+    /**
+     * Copy constructor.  Use this if you have a mostly correct configuration
+     * for your compilation but you want to make a some changes programatically.
+     * An important reason to prefer this approach is that your code will most
+     * likely be forward compatible with future changes to this configuration API.
+     * <p>
+     * An example of this copy constructor at work:
+     * <pre>
+     *    // In all likelihood there is already a configuration in your code's context
+     *    // for you to copy, but for the sake of this example we'll use the global default.
+     *    CompilerConfiguration myConfiguration = new CompilerConfiguration(CompilerConfiguration.DEFAULT);
+     *    myConfiguration.setDebug(true);
+     *</pre>
+     *
+     * @param configuration The configuration to copy.
+     */
+    public CompilerConfiguration(CompilerConfiguration configuration) {
+        setWarningLevel(configuration.getWarningLevel());
+        setOutput(configuration.getOutput());
+        setTargetDirectory(configuration.getTargetDirectory());
+        setClasspathList(new LinkedList<String>(configuration.getClasspath()));
+        setVerbose(configuration.getVerbose());
+        setDebug(configuration.getDebug());
+        setParameters(configuration.getParameters());
+        setTolerance(configuration.getTolerance());
+        setScriptBaseClass(configuration.getScriptBaseClass());
+        setRecompileGroovySource(configuration.getRecompileGroovySource());
+        setMinimumRecompilationInterval(configuration.getMinimumRecompilationInterval());
+        setTargetBytecode(configuration.getTargetBytecode());
+        setDefaultScriptExtension(configuration.getDefaultScriptExtension());
+        setSourceEncoding(configuration.getSourceEncoding());
+        setTargetDirectory(configuration.getTargetDirectory());
+        Map<String, Object> jointCompilationOptions = configuration.getJointCompilationOptions();
+        if (jointCompilationOptions != null) {
+            jointCompilationOptions = new HashMap<String, Object>(jointCompilationOptions);
+        }
+        setJointCompilationOptions(jointCompilationOptions);
+        setPluginFactory(configuration.getPluginFactory());
+        setScriptExtensions(configuration.getScriptExtensions());
+        setOptimizationOptions(new HashMap<String, Boolean>(configuration.getOptimizationOptions()));
+    }
+
+    /**
+     * Sets the Flags to the specified configuration, with defaults
+     * for those not supplied.
+     * Note that those "defaults" here do <em>not</em> include checking the
+     * settings in {@link System#getProperties()} in general, only file.encoding, 
+     * groovy.target.directory and groovy.source.encoding are.
+     * <p>
+     * If you want to set a few flags but keep Groovy's default
+     * configuration behavior then be sure to make your settings in
+     * a Properties that is backed by <code>System.getProperties()</code> (which
+     * is done using this constructor). That might be done like this:
+     * <pre>
+     * Properties myProperties = new Properties(System.getProperties());
+     * myProperties.setProperty("groovy.output.debug", "true");
+     * myConfiguration = new CompilerConfiguration(myProperties);
+     * </pre>
+     * And you also have to contend with a possible SecurityException when
+     * getting the system properties (See {@link java.lang.System#getProperties()}).
+     * A safer approach would be to copy a default
+     * CompilerConfiguration and make your changes there using the setter:
+     * <pre>
+     * // In all likelihood there is already a configuration for you to copy,
+     * // but for the sake of this example we'll use the global default.
+     * CompilerConfiguration myConfiguration = new CompilerConfiguration(CompilerConfiguration.DEFAULT);
+     * myConfiguration.setDebug(true);
+     * </pre>
+     * <p>
+     * <table summary="Groovy Compiler Configuration Properties">
+     *   <tr>
+     *      <th>Property Key</th><th>Get/Set Property Name</th>
+     *   </tr>
+     *      <tr>
+     *      <td><code>"groovy.warnings"</code></td><td>{@link #getWarningLevel}</td></tr>
+     *      <tr><td><code>"groovy.source.encoding"</code></td><td>{@link #getSourceEncoding}</td></tr>
+     *      <tr><td><code>"groovy.target.directory"</code></td><td>{@link #getTargetDirectory}</td></tr>
+     *      <tr><td><code>"groovy.target.bytecode"</code></td><td>{@link #getTargetBytecode}</td></tr>
+     *      <tr><td><code>"groovy.classpath"</code></td><td>{@link #getClasspath}</td></tr>
+     *      <tr><td><code>"groovy.output.verbose"</code></td><td>{@link #getVerbose}</td></tr>
+     *      <tr><td><code>"groovy.output.debug"</code></td><td>{@link #getDebug}</td></tr>
+     *      <tr><td><code>"groovy.errors.tolerance"</code></td><td>{@link #getTolerance}</td></tr>
+     *      <tr><td><code>"groovy.script.extension"</code></td><td>{@link #getDefaultScriptExtension}</td></tr>
+     *      <tr><td><code>"groovy.script.base"</code></td><td>{@link #getScriptBaseClass}</td></tr>
+     *      <tr><td><code>"groovy.recompile"</code></td><td>{@link #getRecompileGroovySource}</td></tr>
+     *      <tr><td><code>"groovy.recompile.minimumInterval"</code></td><td>{@link #getMinimumRecompilationInterval}</td></tr>
+     *      <tr><td>
+     *   </tr>
+     * </table>
+     *
+     * @param configuration The properties to get flag values from.
+     */
+    public CompilerConfiguration(Properties configuration) throws ConfigurationException {
+        this();
+        configure(configuration);
+    }
+
+    /**
+     * Checks if the specified bytecode version string represents a JDK 1.5+ compatible
+     * bytecode version.
+     * @param bytecodeVersion the bytecode version string (1.4, 1.5, 1.6, 1.7 or 1.8)
+     * @return true if the bytecode version is JDK 1.5+
+     */
+    public static boolean isPostJDK5(String bytecodeVersion) {
+        return new BigDecimal(bytecodeVersion).compareTo(new BigDecimal(JDK5)) >= 0;
+    }
+
+    /**
+     * Checks if the specified bytecode version string represents a JDK 1.7+ compatible
+     * bytecode version.
+     * @param bytecodeVersion the bytecode version string (1.4, 1.5, 1.6, 1.7 or 1.8)
+     * @return true if the bytecode version is JDK 1.7+
+     */
+    public static boolean isPostJDK7(String bytecodeVersion) {
+        return new BigDecimal(bytecodeVersion).compareTo(new BigDecimal(JDK7)) >= 0;
+    }
+
+    /**
+     * Method to configure a CompilerConfiguration by using Properties.
+     * For a list of available properties look at {@link #CompilerConfiguration(Properties)}.
+     * @param configuration The properties to get flag values from.
+     */
+    public void configure(Properties configuration) throws ConfigurationException {
+        String text = null;
+        int numeric = 0;
+
+        //
+        // Warning level
+
+        numeric = getWarningLevel();
+        try {
+            text = configuration.getProperty("groovy.warnings", "likely errors");
+            numeric = Integer.parseInt(text);
+        } catch (NumberFormatException e) {
+            text = text.toLowerCase();
+            if (text.equals("none")) {
+                numeric = WarningMessage.NONE;
+            }
+            else if (text.startsWith("likely")) {
+                numeric = WarningMessage.LIKELY_ERRORS;
+            }
+            else if (text.startsWith("possible")) {
+                numeric = WarningMessage.POSSIBLE_ERRORS;
+            }
+            else if (text.startsWith("paranoia")) {
+                numeric = WarningMessage.PARANOIA;
+            }
+            else {
+                throw new ConfigurationException("unrecognized groovy.warnings: " + text);
+            }
+        }
+        setWarningLevel(numeric);
+
+        //
+        // Source file encoding
+        //
+        text = configuration.getProperty("groovy.source.encoding");
+        if (text == null) {
+            text = configuration.getProperty("file.encoding", "US-ASCII");
+        }
+        setSourceEncoding(text);
+
+        //
+        // Target directory for classes
+        //
+        text = configuration.getProperty("groovy.target.directory");
+        if (text != null) setTargetDirectory(text);
+
+        text = configuration.getProperty("groovy.target.bytecode");
+        if (text != null) setTargetBytecode(text);
+
+        //
+        // Classpath
+        //
+        text = configuration.getProperty("groovy.classpath");
+        if (text != null) setClasspath(text);
+
+        //
+        // Verbosity
+        //
+        text = configuration.getProperty("groovy.output.verbose");
+        if (text != null && text.equalsIgnoreCase("true")) setVerbose(true);
+
+        //
+        // Debugging
+        //
+        text = configuration.getProperty("groovy.output.debug");
+        if (text != null && text.equalsIgnoreCase("true")) setDebug(true);
+
+        //
+        // Parameters
+        //
+        setParameters(configuration.getProperty("groovy.parameters") != null);
+
+        //
+        // Tolerance
+        //
+        numeric = 10;
+        try {
+            text = configuration.getProperty("groovy.errors.tolerance", "10");
+            numeric = Integer.parseInt(text);
+        } catch (NumberFormatException e) {
+            throw new ConfigurationException(e);
+        }
+        setTolerance(numeric);
+
+        //
+        // Script Base Class
+        //
+        text = configuration.getProperty("groovy.script.base");
+        if (text!=null) setScriptBaseClass(text);
+
+        //
+        // recompilation options
+        //
+        text = configuration.getProperty("groovy.recompile");
+        if (text != null) {
+            setRecompileGroovySource(text.equalsIgnoreCase("true"));
+        }
+
+        numeric = 100;
+        try {
+            text = configuration.getProperty("groovy.recompile.minimumIntervall");
+            if (text==null) text = configuration.getProperty("groovy.recompile.minimumInterval");
+            if (text!=null) {
+                numeric = Integer.parseInt(text);
+            } else {
+                numeric = 100;
+            }
+        } catch (NumberFormatException e) {
+            throw new ConfigurationException(e);
+        }
+        setMinimumRecompilationInterval(numeric);
+
+        // disabled global AST transformations
+        text = configuration.getProperty("groovy.disabled.global.ast.transformations");
+        if (text!=null) {
+            String[] classNames = text.split(",\\s*}");
+            Set<String> blacklist = new HashSet<String>(Arrays.asList(classNames));
+            setDisabledGlobalASTTransformations(blacklist);
+        }
+    }
+
+    /**
+     * Gets the currently configured warning level. See {@link WarningMessage}
+     * for level details.
+     */
+    public int getWarningLevel() {
+        return this.warningLevel;
+    }
+
+    /**
+     * Sets the warning level. See {@link WarningMessage} for level details.
+     */
+    public void setWarningLevel(int level) {
+        if (level < WarningMessage.NONE || level > WarningMessage.PARANOIA) {
+            this.warningLevel = WarningMessage.LIKELY_ERRORS;
+        }
+        else {
+            this.warningLevel = level;
+        }
+    }
+
+    /**
+     * Gets the currently configured source file encoding.
+     */
+    public String getSourceEncoding() {
+        return this.sourceEncoding;
+    }
+
+    /**
+     * Sets the encoding to be used when reading source files.
+     */
+    public void setSourceEncoding(String encoding) {
+        if (encoding == null) encoding = DEFAULT_SOURCE_ENCODING;
+        this.sourceEncoding = encoding;
+    }
+
+    /**
+     * Gets the currently configured output writer.
+     * @deprecated not used anymore
+     */
+    @Deprecated
+    public PrintWriter getOutput() {
+        return this.output;
+    }
+
+    /**
+     * Sets the output writer.
+     * @deprecated not used anymore, has no effect
+     */
+    @Deprecated
+    public void setOutput(PrintWriter output) {
+        if (output == null) {
+            this.output = new PrintWriter(NullWriter.DEFAULT);
+        }
+        else {
+            this.output = output;
+        }
+    }
+
+    /**
+     * Gets the target directory for writing classes.
+     */
+    public File getTargetDirectory() {
+        return this.targetDirectory;
+    }
+
+    /**
+     * Sets the target directory.
+     */
+    public void setTargetDirectory(String directory) {
+        if (directory != null && directory.length() > 0) {
+            this.targetDirectory = new File(directory);
+        } else {
+            this.targetDirectory = null;
+        }
+    }
+
+    /**
+     * Sets the target directory.
+     */
+    public void setTargetDirectory(File directory) {
+        this.targetDirectory = directory;
+    }
+
+    /**
+     * @return the classpath
+     */
+    public List<String> getClasspath() {
+        return this.classpath;
+    }
+
+    /**
+     * Sets the classpath.
+     */
+    public void setClasspath(String classpath) {
+        this.classpath = new LinkedList<String>();
+        StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
+        while (tokenizer.hasMoreTokens()) {
+            this.classpath.add(tokenizer.nextToken());
+        }
+    }
+
+    /**
+     * sets the classpath using a list of Strings
+     * @param parts list of strings containing the classpath parts
+     */
+    public void setClasspathList(List<String> parts) {
+        this.classpath = new LinkedList<String>(parts);
+    }
+
+    /**
+     * Returns true if verbose operation has been requested.
+     */
+    public boolean getVerbose() {
+        return this.verbose;
+    }
+
+    /**
+     * Turns verbose operation on or off.
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    /**
+     * Returns true if debugging operation has been requested.
+     */
+    public boolean getDebug() {
+        return this.debug;
+    }
+
+    /**
+     * Returns true if parameter metadata generation has been enabled.
+     */
+    public boolean getParameters() {
+        return this.parameters;
+    }
+
+    /**
+     * Turns debugging operation on or off.
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * Turns parameter metadata generation on or off.
+     */
+    public void setParameters(boolean parameters) {
+        this.parameters = parameters;
+    }
+
+    /**
+     * Returns the requested error tolerance.
+     */
+    public int getTolerance() {
+        return this.tolerance;
+    }
+
+    /**
+     * Sets the error tolerance, which is the number of
+     * non-fatal errors (per unit) that should be tolerated before
+     * compilation is aborted.
+     */
+    public void setTolerance(int tolerance) {
+        this.tolerance = tolerance;
+    }
+
+    /**
+     * Gets the name of the base class for scripts.  It must be a subclass
+     * of Script.
+     */
+    public String getScriptBaseClass() {
+        return this.scriptBaseClass;
+    }
+
+    /**
+     * Sets the name of the base class for scripts.  It must be a subclass
+     * of Script.
+     */
+    public void setScriptBaseClass(String scriptBaseClass) {
+        this.scriptBaseClass = scriptBaseClass;
+    }
+
+    public ParserPluginFactory getPluginFactory() {
+        if (pluginFactory == null) {
+            pluginFactory = ParserVersion.V_2 == parserVersion
+                                ? ParserPluginFactory.antlr2()
+                                : ParserPluginFactory.antlr4();
+        }
+        return pluginFactory;
+    }
+
+    public void setPluginFactory(ParserPluginFactory pluginFactory) {
+        this.pluginFactory = pluginFactory;
+    }
+
+    public void setScriptExtensions(Set<String> scriptExtensions) {
+        if(scriptExtensions == null) scriptExtensions = new LinkedHashSet<String>();
+        this.scriptExtensions = scriptExtensions;
+    }
+
+    public Set<String> getScriptExtensions() {
+        if(scriptExtensions == null || scriptExtensions.isEmpty()) {
+            /*
+             *  this happens
+             *  *    when groovyc calls FileSystemCompiler in forked mode, or
+             *  *    when FileSystemCompiler is run from the command line directly, or
+             *  *    when groovy was not started using groovyc or FileSystemCompiler either
+             */
+            scriptExtensions = SourceExtensionHandler.getRegisteredExtensions(
+                    this.getClass().getClassLoader());
+        }
+        return scriptExtensions;
+    }
+
+    public String getDefaultScriptExtension() {
+        return defaultScriptExtension;
+    }
+
+
+    public void setDefaultScriptExtension(String defaultScriptExtension) {
+        this.defaultScriptExtension = defaultScriptExtension;
+    }
+
+    public void setRecompileGroovySource(boolean recompile) {
+        recompileGroovySource = recompile;
+    }
+
+    public boolean getRecompileGroovySource(){
+        return recompileGroovySource;
+    }
+
+    public void setMinimumRecompilationInterval(int time) {
+        minimumRecompilationInterval = Math.max(0,time);
+    }
+
+    public int getMinimumRecompilationInterval() {
+        return minimumRecompilationInterval;
+    }
+
+    /**
+     * Allow setting the bytecode compatibility. The parameter can take
+     * one of the values <tt>1.7</tt>, <tt>1.6</tt>, <tt>1.5</tt> or <tt>1.4</tt>.
+     * If wrong parameter then the value will default to VM determined version.
+     *
+     * @param version the bytecode compatibility mode
+     */
+    public void setTargetBytecode(String version) {
+        for (String allowedJdk : ALLOWED_JDKS) {
+            if (allowedJdk.equals(version)) {
+                this.targetBytecode = version;
+            }
+        }
+    }
+
+    /**
+     * Retrieves the compiler bytecode compatibility mode.
+     *
+     * @return bytecode compatibility mode. Can be either <tt>1.5</tt> or <tt>1.4</tt>.
+     */
+    public String getTargetBytecode() {
+        return this.targetBytecode;
+    }
+
+    private static String getVMVersion() {
+        return POST_JDK5;
+    }
+
+    /**
+     * Gets the joint compilation options for this configuration.
+     * @return the options
+     */
+    public Map<String, Object> getJointCompilationOptions() {
+        return jointCompilationOptions;
+    }
+
+    /**
+     * Sets the joint compilation options for this configuration.
+     * Using null will disable joint compilation.
+     * @param options the options
+     */
+    public void setJointCompilationOptions(Map<String, Object> options) {
+        jointCompilationOptions = options;
+    }
+
+    /**
+     * Gets the optimization options for this configuration.
+     * @return the options (always not null)
+     */
+    public Map<String, Boolean> getOptimizationOptions() {
+        return optimizationOptions;
+    }
+
+    /**
+     * Sets the optimization options for this configuration.
+     * No entry or a true for that entry means to enable that optimization,
+     * a false means the optimization is disabled.
+     * Valid keys are "all" and "int".
+     * @param options the options.
+     * @throws IllegalArgumentException if the options are null
+     */
+    public void setOptimizationOptions(Map<String, Boolean> options) {
+        if (options==null) throw new IllegalArgumentException("provided option map must not be null");
+        optimizationOptions = options;
+    }
+
+    /**
+     * Adds compilation customizers to the compilation process. A compilation customizer is a class node
+     * operation which performs various operations going from adding imports to access control.
+     * @param customizers the list of customizers to be added
+     * @return this configuration instance
+     */
+    public CompilerConfiguration addCompilationCustomizers(CompilationCustomizer... customizers) {
+        if (customizers==null) throw new IllegalArgumentException("provided customizers list must not be null");
+        compilationCustomizers.addAll(Arrays.asList(customizers));
+        return this;
+    }
+
+    /**
+     * Returns the list of compilation customizers.
+     * @return the customizers (always not null)
+     */
+    public List<CompilationCustomizer> getCompilationCustomizers() {
+        return compilationCustomizers;
+    }
+
+    /**
+     * Returns the list of disabled global AST transformation class names.
+     * @return a list of global AST transformation fully qualified class names
+     */
+    public Set<String> getDisabledGlobalASTTransformations() {
+        return disabledGlobalASTTransformations;
+    }
+
+    /**
+     * Disables global AST transformations. In order to avoid class loading side effects, it is not recommended
+     * to use MyASTTransformation.class.getName() by directly use the class name as a string. Disabled AST transformations
+     * only apply to automatically loaded global AST transformations, that is to say transformations defined in a
+     * META-INF/org.codehaus.groovy.transform.ASTTransformation file. If you explicitly add a global AST transformation
+     * in your compilation process, for example using the {@link org.codehaus.groovy.control.customizers.ASTTransformationCustomizer} or
+     * using a {@link org.codehaus.groovy.control.CompilationUnit.PrimaryClassNodeOperation}, then nothing will prevent
+     * the transformation from being loaded.
+     * @param disabledGlobalASTTransformations a set of fully qualified class names of global AST transformations
+     * which should not be loaded.
+     */
+    public void setDisabledGlobalASTTransformations(final Set<String> disabledGlobalASTTransformations) {
+        this.disabledGlobalASTTransformations = disabledGlobalASTTransformations;
+    }
+
+    public BytecodeProcessor getBytecodePostprocessor() {
+        return bytecodePostprocessor;
+    }
+
+    public void setBytecodePostprocessor(final BytecodeProcessor bytecodePostprocessor) {
+        this.bytecodePostprocessor = bytecodePostprocessor;
+    }
+
+    public ParserVersion getParserVersion() {
+        return this.parserVersion;
+    }
+
+    public void setParserVersion(ParserVersion parserVersion) {
+        this.parserVersion = parserVersion;
+    }
+
+    /**
+     * Check whether invoke dynamic enabled
+     * @return the result
+     */
+    public boolean isIndyEnabled() {
+        Boolean indyEnabled = this.getOptimizationOptions().get(INVOKEDYNAMIC);
+
+        if (null == indyEnabled) {
+            return false;
+        }
+
+        return indyEnabled;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/control/ConfigurationException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/ConfigurationException.java b/src/main/java/org/codehaus/groovy/control/ConfigurationException.java
new file mode 100644
index 0000000..3d0658a
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/ConfigurationException.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.control;
+
+import org.codehaus.groovy.GroovyExceptionInterface;
+
+
+
+
+/**
+ *  Thrown when configuration data is invalid.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ */
+
+public class ConfigurationException extends RuntimeException implements GroovyExceptionInterface
+{
+    
+  //---------------------------------------------------------------------------
+  // CONSTRUCTION AND SUCH
+
+    protected Exception cause;   // The phase in which the failures occurred
+
+    
+   /**
+    *  Initializes the exception from a cause exception.
+    */
+    
+    public ConfigurationException( Exception cause ) 
+    {
+        super( cause.getMessage() );
+        this.cause = cause;
+    }
+    
+    
+   /**
+    *  Initializes the exception with just a message.
+    */
+    
+    public ConfigurationException( String message )
+    {
+        super( message );
+    }
+
+    
+    
+   /**
+    *  Returns the causing exception, if available.
+    */
+    
+    public Throwable getCause()
+    {
+        return cause;
+    }
+    
+    
+   /**
+    *  Its always fatal.
+    */
+    
+    public boolean isFatal()
+    {
+        return true;
+    }
+    
+    
+    
+   /**
+    *  Set fatal is just ignored.
+    */
+    
+    public void setFatal( boolean fatal )
+    {
+    }
+    
+}