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/14 21:56:41 UTC

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

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/util/LoggableClassVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/util/LoggableClassVisitor.java b/src/main/java/org/codehaus/groovy/classgen/asm/util/LoggableClassVisitor.java
new file mode 100644
index 0000000..b794235
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/util/LoggableClassVisitor.java
@@ -0,0 +1,34 @@
+/*
+ *  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.classgen.asm.util;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.util.TraceClassVisitor;
+
+/**
+ * A ClassVisitor proxy, which can log bytecode generation
+ *
+ * @since 2.5.0
+ */
+public class LoggableClassVisitor extends ClassVisitor {
+    public LoggableClassVisitor(final ClassVisitor cv) {
+        super(Opcodes.ASM6, new TraceClassVisitor(cv, new LoggableTextifier(), null));
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/asm/util/LoggableTextifier.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/util/LoggableTextifier.java b/src/main/java/org/codehaus/groovy/classgen/asm/util/LoggableTextifier.java
new file mode 100644
index 0000000..e5b0dea
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/util/LoggableTextifier.java
@@ -0,0 +1,438 @@
+/*
+ *  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.classgen.asm.util;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.TypePath;
+import org.objectweb.asm.util.Printer;
+import org.objectweb.asm.util.Textifier;
+
+import java.util.List;
+
+/**
+ * Logging bytecode generation, which can make debugging easy
+ *
+ * @since 2.5.0
+ */
+public class LoggableTextifier extends Textifier {
+//    private static final Logger LOGGER = Logger.getLogger(LoggableTextifier.class.getName());
+    private int loggedLineCnt = 0;
+
+    public LoggableTextifier() {
+        super(Opcodes.ASM6);
+    }
+
+    @Override
+    protected Textifier createTextifier() {
+        return new LoggableTextifier();
+    }
+
+    protected void log() {
+        int textSize = text.size();
+
+        for (int i = loggedLineCnt; i < textSize; i++) {
+            Object bc = text.get(i);
+
+            if (bc instanceof List && 0 == ((List) bc).size()) {
+                continue;
+            }
+
+            System.out.print(bc);
+        }
+
+        loggedLineCnt = textSize;
+    }
+
+
+    @Override
+    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+        super.visit(version, access, name, signature, superName, interfaces);
+        log();
+    }
+
+    @Override
+    public void visitSource(String file, String debug) {
+        super.visitSource(file, debug);
+        log();
+    }
+
+    @Override
+    public Printer visitModule(final String name, final int access, final String version) {
+        Printer p = super.visitModule(name, access, version);
+        log();
+        return p;
+    }
+
+    @Override
+    public void visitOuterClass(String owner, String name, String desc) {
+        super.visitOuterClass(owner, name, desc);
+        log();
+    }
+
+    @Override
+    public Textifier visitClassAnnotation(String desc, boolean visible) {
+        Textifier t = super.visitClassAnnotation(desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public Printer visitClassTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
+        Printer t = super.visitClassTypeAnnotation(typeRef, typePath, desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public void visitClassAttribute(Attribute attr) {
+        super.visitClassAttribute(attr);
+        log();
+    }
+
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+        super.visitInnerClass(name, outerName, innerName, access);
+        log();
+    }
+
+    @Override
+    public Textifier visitField(int access, String name, String desc, String signature, Object value) {
+        Textifier t = super.visitField(access, name, desc, signature, value);
+        log();
+        return t;
+    }
+
+    @Override
+    public Textifier visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+        Textifier t = super.visitMethod(access, name, desc, signature, exceptions);
+        log();
+        return t;
+    }
+
+    @Override
+    public void visitClassEnd() {
+        super.visitClassEnd();
+        log();
+    }
+
+    @Override
+    public void visitRequire(String require, int access, String version) {
+        super.visitRequire(require, access, version);
+        log();
+    }
+
+    @Override
+    public void visitExport(String export, int access, String... modules) {
+        super.visitExport(export, access, modules);
+        log();
+    }
+
+    @Override
+    public void visitUse(String use) {
+        super.visitUse(use);
+        log();
+    }
+
+    @Override
+    public void visitProvide(String provide, String... providers) {
+        super.visitProvide(provide, providers);
+        log();
+    }
+
+    @Override
+    public void visitModuleEnd() {
+        super.visitModuleEnd();
+        log();
+    }
+
+    @Override
+    public void visit(String name, Object value) {
+        super.visit(name, value);
+        log();
+    }
+
+    @Override
+    public void visitEnum(String name, String desc, String value) {
+        super.visitEnum(name, desc, value);
+        log();
+    }
+
+    @Override
+    public Textifier visitAnnotation(String name, String desc) {
+        Textifier t = super.visitAnnotation(name, desc);
+        log();
+        return t;
+    }
+
+    @Override
+    public Textifier visitArray(String name) {
+        Textifier t = super.visitArray(name);
+        log();
+        return t;
+    }
+
+    @Override
+    public void visitAnnotationEnd() {
+        super.visitAnnotationEnd();
+        log();
+    }
+
+    @Override
+    public Textifier visitFieldAnnotation(String desc, boolean visible) {
+        Textifier t = super.visitFieldAnnotation(desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public Printer visitFieldTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
+        Printer t = super.visitFieldTypeAnnotation(typeRef, typePath, desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public void visitFieldAttribute(Attribute attr) {
+        super.visitFieldAttribute(attr);
+        log();
+    }
+
+    @Override
+    public void visitFieldEnd() {
+        super.visitFieldEnd();
+        log();
+    }
+
+    @Override
+    public void visitParameter(String name, int access) {
+        super.visitParameter(name, access);
+        log();
+    }
+
+    @Override
+    public Textifier visitAnnotationDefault() {
+        Textifier t = super.visitAnnotationDefault();
+        log();
+        return t;
+    }
+
+    @Override
+    public Textifier visitMethodAnnotation(String desc, boolean visible) {
+        Textifier t = super.visitMethodAnnotation(desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public Printer visitMethodTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
+        Printer t = super.visitMethodTypeAnnotation(typeRef, typePath, desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public Textifier visitParameterAnnotation(int parameter, String desc, boolean visible) {
+        Textifier t = super.visitParameterAnnotation(parameter, desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public void visitMethodAttribute(Attribute attr) {
+        super.visitMethodAttribute(attr);
+        log();
+    }
+
+    @Override
+    public void visitCode() {
+        super.visitCode();
+        log();
+    }
+
+    @Override
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+        super.visitFrame(type, nLocal, local, nStack, stack);
+        log();
+    }
+
+    @Override
+    public void visitInsn(int opcode) {
+        super.visitInsn(opcode);
+        log();
+    }
+
+    @Override
+    public void visitIntInsn(int opcode, int operand) {
+        super.visitIntInsn(opcode, operand);
+        log();
+    }
+
+    @Override
+    public void visitVarInsn(int opcode, int var) {
+        super.visitVarInsn(opcode, var);
+        log();
+    }
+
+    @Override
+    public void visitTypeInsn(int opcode, String type) {
+        super.visitTypeInsn(opcode, type);
+        log();
+    }
+
+    @Override
+    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+        super.visitFieldInsn(opcode, owner, name, desc);
+        log();
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        super.visitMethodInsn(opcode, owner, name, desc);
+        log();
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+        super.visitMethodInsn(opcode, owner, name, desc, itf);
+        log();
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
+        super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+        log();
+    }
+
+    @Override
+    public void visitJumpInsn(int opcode, Label label) {
+        super.visitJumpInsn(opcode, label);
+        log();
+    }
+
+    @Override
+    public void visitLabel(Label label) {
+        super.visitLabel(label);
+        log();
+    }
+
+    @Override
+    public void visitLdcInsn(Object cst) {
+        super.visitLdcInsn(cst);
+        log();
+    }
+
+    @Override
+    public void visitIincInsn(int var, int increment) {
+        super.visitIincInsn(var, increment);
+        log();
+    }
+
+    @Override
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
+        super.visitTableSwitchInsn(min, max, dflt, labels);
+        log();
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        super.visitLookupSwitchInsn(dflt, keys, labels);
+        log();
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        super.visitMultiANewArrayInsn(desc, dims);
+        log();
+    }
+
+    @Override
+    public Printer visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
+        Printer t = super.visitInsnAnnotation(typeRef, typePath, desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+        super.visitTryCatchBlock(start, end, handler, type);
+        log();
+    }
+
+    @Override
+    public Printer visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
+        Printer t = super.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
+        super.visitLocalVariable(name, desc, signature, start, end, index);
+        log();
+    }
+
+    @Override
+    public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) {
+        Printer t = super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public void visitLineNumber(int line, Label start) {
+        super.visitLineNumber(line, start);
+        log();
+    }
+
+    @Override
+    public void visitMaxs(int maxStack, int maxLocals) {
+        super.visitMaxs(maxStack, maxLocals);
+        log();
+    }
+
+    @Override
+    public void visitMethodEnd() {
+        super.visitMethodEnd();
+        log();
+    }
+
+    @Override
+    public Textifier visitAnnotation(String desc, boolean visible) {
+        Textifier t = super.visitAnnotation(desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public Textifier visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
+        Textifier t = super.visitTypeAnnotation(typeRef, typePath, desc, visible);
+        log();
+        return t;
+    }
+
+    @Override
+    public void visitAttribute(Attribute attr) {
+        super.visitAttribute(attr);
+        log();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/package.html
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/package.html b/src/main/java/org/codehaus/groovy/classgen/package.html
new file mode 100644
index 0000000..a72224a
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/package.html
@@ -0,0 +1,28 @@
+<!--
+
+     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.
+
+-->
+<html>
+  <head>
+    <title>package org.codehaus.groovy.classgen.*</title>
+  </head>
+  <body>
+    <p>Generates Java classes for Groovy classes using ASM.</p>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/cli/GroovyPosixParser.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/cli/GroovyPosixParser.java b/src/main/java/org/codehaus/groovy/cli/GroovyPosixParser.java
new file mode 100644
index 0000000..e583ac9
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/cli/GroovyPosixParser.java
@@ -0,0 +1,281 @@
+/*
+ *  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.cli;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.Parser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This is a hacked version of Commons CLI 1.2 PosixParser with some bug fixes added.
+ * We aren't aware of any use cases where it isn't now preferable to use the
+ * Commons CLI 1.3 DefaultParser but this class is retained for the time being for
+ * backwards compatibility if someone is relying on this class's exact functionality.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @author Paul King (Groovy hacks/fixes)
+ * @deprecated use the DefaultParser from Commons CLI
+ */
+@Deprecated
+public class GroovyPosixParser extends Parser
+{
+    /** holder for flattened tokens */
+    private List tokens = new ArrayList();
+
+    /** specifies if bursting should continue */
+    private boolean eatTheRest;
+
+    /** holder for the current option */
+    private Option currentOption;
+
+    /** the command line Options */
+    private Options options;
+
+    /**
+     * Resets the members to their original state i.e. remove
+     * all of <code>tokens</code> entries and set <code>eatTheRest</code>
+     * to false.
+     */
+    private void init()
+    {
+        eatTheRest = false;
+        tokens.clear();
+    }
+
+    /**
+     * An implementation of {@link Parser}'s abstract
+     * {@link Parser#flatten(Options,String[],boolean) flatten} method.
+     * <p>
+     * The following are the rules used by this flatten method.
+     * <ol>
+     *  <li>if <code>stopAtNonOption</code> is <b>true</b> then do not
+     *  burst anymore of <code>arguments</code> entries, just add each
+     *  successive entry without further processing.  Otherwise, ignore
+     *  <code>stopAtNonOption</code>.</li>
+     *  <li>if the current <code>arguments</code> entry is "<b>--</b>"
+     *  just add the entry to the list of processed tokens</li>
+     *  <li>if the current <code>arguments</code> entry is "<b>-</b>"
+     *  just add the entry to the list of processed tokens</li>
+     *  <li>if the current <code>arguments</code> entry is two characters
+     *  in length and the first character is "<b>-</b>" then check if this
+     *  is a valid {@link Option} id.  If it is a valid id, then add the
+     *  entry to the list of processed tokens and set the current {@link Option}
+     *  member.  If it is not a valid id and <code>stopAtNonOption</code>
+     *  is true, then the remaining entries are copied to the list of
+     *  processed tokens.  Otherwise, the current entry is ignored.</li>
+     *  <li>if the current <code>arguments</code> entry is more than two
+     *  characters in length and the first character is "<b>-</b>" then
+     *  we need to burst the entry to determine its constituents.  For more
+     *  information on the bursting algorithm see
+     *  {@link GroovyPosixParser#burstToken(String, boolean) burstToken}.</li>
+     *  <li>if the current <code>arguments</code> entry is not handled
+     *  by any of the previous rules, then the entry is added to the list
+     *  of processed tokens.</li>
+     * </ol>
+     *
+     * @param options The command line {@link Options}
+     * @param arguments The command line arguments to be parsed
+     * @param stopAtNonOption Specifies whether to stop flattening when an non option is found.
+     * @return The flattened <code>arguments</code> String array.
+     */
+    protected String[] flatten(Options options, String[] arguments, boolean stopAtNonOption) {
+        init();
+        this.options = options;
+
+        // an iterator for the command line tokens
+        Iterator iter = Arrays.asList(arguments).iterator();
+
+        // process each command line token
+        while (iter.hasNext())
+        {
+            // get the next command line token
+            String token = (String) iter.next();
+
+            // handle long option --foo or --foo=bar
+            if (token.startsWith("--"))
+            {
+                int pos = token.indexOf('=');
+                String opt = pos == -1 ? token : token.substring(0, pos); // --foo
+
+                if (!options.hasOption(opt))
+                {
+                    processNonOptionToken(token, stopAtNonOption);
+                }
+                else
+                {
+                    tokens.add(opt);
+                    if (pos != -1)
+                    {
+                        tokens.add(token.substring(pos + 1));
+                    } else {
+                        currentOption = options.getOption(opt);
+                    }
+                }
+            }
+
+            // single hyphen
+            else if ("-".equals(token))
+            {
+                tokens.add(token);
+            }
+            else if (token.startsWith("-"))
+            {
+                if (token.length() == 2 || options.hasOption(token))
+                {
+                    processOptionToken(token, stopAtNonOption);
+                }
+                // requires bursting
+                else
+                {
+                    burstToken(token, stopAtNonOption);
+                }
+            }
+            else
+            {
+                processNonOptionToken(token, stopAtNonOption);
+            }
+
+            gobble(iter);
+        }
+
+        return (String[]) tokens.toArray(new String[tokens.size()]);
+    }
+
+    /**
+     * Adds the remaining tokens to the processed tokens list.
+     *
+     * @param iter An iterator over the remaining tokens
+     */
+    private void gobble(Iterator iter)
+    {
+        if (eatTheRest)
+        {
+            while (iter.hasNext())
+            {
+                tokens.add(iter.next());
+            }
+        }
+    }
+
+    /**
+     * Add the special token "<b>--</b>" and the current <code>value</code>
+     * to the processed tokens list. Then add all the remaining
+     * <code>argument</code> values to the processed tokens list.
+     *
+     * @param value The current token
+     */
+    private void processNonOptionToken(String value, boolean stopAtNonOption)
+    {
+        if (stopAtNonOption && (currentOption == null || !currentOption.hasArg()))
+        {
+            eatTheRest = true;
+            tokens.add("--");
+        }
+
+        tokens.add(value);
+        currentOption = null;
+    }
+
+    /**
+     * If an {@link Option} exists for <code>token</code> then
+     * add the token to the processed list.
+     * <p>
+     * If an {@link Option} does not exist and <code>stopAtNonOption</code>
+     * is set then add the remaining tokens to the processed tokens list
+     * directly.
+     *
+     * @param token The current option token
+     * @param stopAtNonOption Specifies whether flattening should halt at the first non option.
+     */
+    private void processOptionToken(String token, boolean stopAtNonOption) {
+        if (stopAtNonOption && !options.hasOption(token))
+        {
+            eatTheRest = true;
+        }
+
+        if (options.hasOption(token)) {
+            currentOption = options.getOption(token);
+        } else {
+            currentOption = null;
+        }
+
+        tokens.add(token);
+    }
+
+    /**
+     * Breaks <code>token</code> into its constituent parts
+     * using the following algorithm.
+     *
+     * <ul>
+     *  <li>ignore the first character ("<b>-</b>")</li>
+     *  <li>foreach remaining character check if an {@link Option}
+     *  exists with that id.</li>
+     *  <li>if an {@link Option} does exist then add that character
+     *  prepended with "<b>-</b>" to the list of processed tokens.</li>
+     *  <li>if the {@link Option} can have an argument value and there
+     *  are remaining characters in the token then add the remaining
+     *  characters as a token to the list of processed tokens.</li>
+     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
+     *  <code>stopAtNonOption</code> <b>IS</b> set then add the special token
+     *  "<b>--</b>" followed by the remaining characters and also
+     *  the remaining tokens directly to the processed tokens list.</li>
+     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
+     *  <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
+     *  character prepended with "<b>-</b>".</li>
+     * </ul>
+     *
+     * @param token The current token to be <b>burst</b>
+     * @param stopAtNonOption Specifies whether to stop processing
+     * at the first non-Option encountered.
+     */
+    protected void burstToken(String token, boolean stopAtNonOption)
+    {
+        for (int i = 1; i < token.length(); i++)
+        {
+            String ch = String.valueOf(token.charAt(i));
+
+            if (options.hasOption(ch))
+            {
+                tokens.add("-" + ch);
+                currentOption = options.getOption(ch);
+
+                if (currentOption.hasArg() && (token.length() != (i + 1)))
+                {
+                    tokens.add(token.substring(i + 1));
+                    break;
+                }
+            }
+            else if (stopAtNonOption)
+            {
+                processNonOptionToken(token.substring(i), true);
+                break;
+            }
+            else
+            {
+                tokens.add(token);
+                break;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/ASTTransformationsContext.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/ASTTransformationsContext.java b/src/main/java/org/codehaus/groovy/control/ASTTransformationsContext.java
new file mode 100644
index 0000000..ae95275
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/ASTTransformationsContext.java
@@ -0,0 +1,53 @@
+/*
+ *  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 java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Stores state information about global AST transformations applied to a compilation unit.
+ *
+ * @author Cedric Champeau
+*/
+public class ASTTransformationsContext {
+    protected final GroovyClassLoader transformLoader;  // Classloader for global and local transforms
+
+    protected final CompilationUnit compilationUnit; // The compilation unit global AST transformations are applied on
+    protected final Set<String> globalTransformNames = new HashSet<String>(); // collected AST transformation names
+
+    public ASTTransformationsContext(final CompilationUnit compilationUnit, final GroovyClassLoader transformLoader) {
+        this.compilationUnit = compilationUnit;
+        this.transformLoader = transformLoader;
+    }
+
+    public CompilationUnit getCompilationUnit() {
+        return compilationUnit;
+    }
+
+    public Set<String> getGlobalTransformNames() {
+        return globalTransformNames;
+    }
+
+    public GroovyClassLoader getTransformLoader() {
+        return transformLoader;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/AnnotationConstantsVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/AnnotationConstantsVisitor.java b/src/main/java/org/codehaus/groovy/control/AnnotationConstantsVisitor.java
new file mode 100644
index 0000000..b1610df
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/AnnotationConstantsVisitor.java
@@ -0,0 +1,133 @@
+/*
+ *  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.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.classgen.Verifier;
+
+import java.math.BigDecimal;
+
+/**
+ * Visitor to resolve constants in annotation definitions.
+ *
+ * @author Paul King
+ */
+public class AnnotationConstantsVisitor extends ClassCodeVisitorSupport {
+    private SourceUnit source;
+    private boolean inAnnotationDef;
+
+    public void visitClass(ClassNode node, SourceUnit source) {
+        this.source = source;
+        this.inAnnotationDef = node.isAnnotationDefinition();
+        super.visitClass(node);
+        this.inAnnotationDef = false;
+    }
+
+    @Override
+    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+        if (!inAnnotationDef) return;
+        visitStatement(node.getFirstStatement(), node.getReturnType());
+    }
+
+    private static void visitStatement(Statement statement, ClassNode returnType) {
+        if (statement instanceof ReturnStatement) {
+            // normal path
+            ReturnStatement rs = (ReturnStatement) statement;
+            rs.setExpression(transformConstantExpression(rs.getExpression(), returnType));
+        } else if (statement instanceof ExpressionStatement) {
+            // path for JavaStubGenerator
+            ExpressionStatement es = (ExpressionStatement) statement;
+            es.setExpression(transformConstantExpression(es.getExpression(), returnType));
+        }
+    }
+
+    private static Expression transformConstantExpression(Expression val, ClassNode returnType) {
+        ClassNode returnWrapperType = ClassHelper.getWrapper(returnType);
+        if (val instanceof ConstantExpression) {
+            Expression result = revertType(val, returnWrapperType);
+            if (result != null) {
+                return result;
+            }
+            return val;
+        }
+        if (val instanceof CastExpression) {
+            CastExpression castExp = (CastExpression) val;
+            Expression castee = castExp.getExpression();
+            if (castee instanceof ConstantExpression) {
+                if (ClassHelper.getWrapper(castee.getType()).isDerivedFrom(returnWrapperType)) {
+                    return castee;
+                }
+                Expression result = revertType(castee, returnWrapperType);
+                if (result != null) {
+                    return result;
+                }
+                return castee;
+            }
+        }
+        return val;
+    }
+
+    private static Expression revertType(Expression val, ClassNode returnWrapperType) {
+        ConstantExpression ce = (ConstantExpression) val;
+        if (ClassHelper.Character_TYPE.equals(returnWrapperType) && ClassHelper.STRING_TYPE.equals(val.getType())) {
+            return configure(val, Verifier.transformToPrimitiveConstantIfPossible((ConstantExpression) val));
+        }
+        ClassNode valWrapperType = ClassHelper.getWrapper(val.getType());
+        if (ClassHelper.Integer_TYPE.equals(valWrapperType)) {
+            Integer i = (Integer) ce.getValue();
+            if (ClassHelper.Character_TYPE.equals(returnWrapperType)) {
+                return configure(val, new ConstantExpression((char) i.intValue(), true));
+            }
+            if (ClassHelper.Short_TYPE.equals(returnWrapperType)) {
+                return configure(val, new ConstantExpression(i.shortValue(), true));
+            }
+            if (ClassHelper.Byte_TYPE.equals(returnWrapperType)) {
+                return configure(val, new ConstantExpression(i.byteValue(), true));
+            }
+        }
+        if (ClassHelper.BigDecimal_TYPE.equals(valWrapperType)) {
+            BigDecimal bd = (BigDecimal) ce.getValue();
+            if (ClassHelper.Float_TYPE.equals(returnWrapperType)) {
+                return configure(val, new ConstantExpression(bd.floatValue(), true));
+            }
+            if (ClassHelper.Double_TYPE.equals(returnWrapperType)) {
+                return configure(val, new ConstantExpression(bd.doubleValue(), true));
+            }
+        }
+        return null;
+    }
+
+    private static Expression configure(Expression orig, Expression result) {
+        result.setSourcePosition(orig);
+        return result;
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return source;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/BytecodeProcessor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/BytecodeProcessor.java b/src/main/java/org/codehaus/groovy/control/BytecodeProcessor.java
new file mode 100644
index 0000000..6ef8c30
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/BytecodeProcessor.java
@@ -0,0 +1,23 @@
+/*
+ *  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;
+
+public interface BytecodeProcessor {
+    byte[] processBytecode(String name, byte[] original);
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/ClassNodeResolver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/ClassNodeResolver.java b/src/main/java/org/codehaus/groovy/control/ClassNodeResolver.java
new file mode 100644
index 0000000..3b64333
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/ClassNodeResolver.java
@@ -0,0 +1,345 @@
+/*
+ *  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 org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.decompiled.AsmDecompiler;
+import org.codehaus.groovy.ast.decompiled.AsmReferenceResolver;
+import org.codehaus.groovy.ast.decompiled.DecompiledClassNode;
+import org.codehaus.groovy.classgen.Verifier;
+import org.objectweb.asm.Opcodes;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class is used as a pluggable way to resolve class names.
+ * An instance of this class has to be added to {@link CompilationUnit} using 
+ * {@link CompilationUnit#setClassNodeResolver(ClassNodeResolver)}. The 
+ * CompilationUnit will then set the resolver on the {@link ResolveVisitor} each 
+ * time new. The ResolveVisitor will prepare name lookup and then finally ask
+ * the resolver if the class exists. This resolver then can return either a 
+ * SourceUnit or a ClassNode. In case of a SourceUnit the compiler is notified
+ * that a new source is to be added to the compilation queue. In case of a
+ * ClassNode no further action than the resolving is done. The lookup result
+ * is stored in the helper class {@link LookupResult}. This class provides a
+ * class cache to cache lookups. If you don't want this, you have to override
+ * the methods {@link ClassNodeResolver#cacheClass(String, ClassNode)} and 
+ * {@link ClassNodeResolver#getFromClassCache(String)}. Custom lookup logic is
+ * supposed to go into the method 
+ * {@link ClassNodeResolver#findClassNode(String, CompilationUnit)} while the 
+ * entry method is {@link ClassNodeResolver#resolveName(String, CompilationUnit)}
+ * 
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class ClassNodeResolver {
+
+    /**
+     * Helper class to return either a SourceUnit or ClassNode.
+     * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+     */
+    public static class LookupResult {
+        private final SourceUnit su;
+        private final ClassNode cn;
+        /**
+         * creates a new LookupResult. You are not supposed to supply
+         * a SourceUnit and a ClassNode at the same time
+         */
+        public LookupResult(SourceUnit su, ClassNode cn) {
+            this.su = su;
+            this.cn = cn;
+            if (su==null && cn==null) throw new IllegalArgumentException("Either the SourceUnit or the ClassNode must not be null.");
+            if (su!=null && cn!=null) throw new IllegalArgumentException("SourceUnit and ClassNode cannot be set at the same time.");
+        }
+        /**
+         * returns true if a ClassNode is stored
+         */
+        public boolean isClassNode() { return cn!=null; }
+        /**
+         * returns true if a SourecUnit is stored
+         */
+        public boolean isSourceUnit() { return su!=null; }
+        /**
+         * returns the SourceUnit
+         */
+        public SourceUnit getSourceUnit() { return su; }
+        /**
+         * returns the ClassNode
+         */
+        public ClassNode getClassNode() { return cn; }
+    }
+
+    // Map to store cached classes
+    private final Map<String,ClassNode> cachedClasses = new HashMap();
+    /**
+     * Internal helper used to indicate a cache hit for a class that does not exist. 
+     * This way further lookups through a slow {@link #findClassNode(String, CompilationUnit)} 
+     * path can be avoided.
+     * WARNING: This class is not to be used outside of ClassNodeResolver.
+     */
+    protected static final ClassNode NO_CLASS = new ClassNode("NO_CLASS", Opcodes.ACC_PUBLIC,ClassHelper.OBJECT_TYPE){
+        public void setRedirect(ClassNode cn) {
+            throw new GroovyBugError("This is a dummy class node only! Never use it for real classes.");
+        }
+    };
+    
+    /**
+     * Resolves the name of a class to a SourceUnit or ClassNode. If no
+     * class or source is found this method returns null. A lookup is done
+     * by first asking the cache if there is an entry for the class already available
+     * to then call {@link #findClassNode(String, CompilationUnit)}. The result 
+     * of that method call will be cached if a ClassNode is found. If a SourceUnit
+     * is found, this method will not be asked later on again for that class, because
+     * ResolveVisitor will first ask the CompilationUnit for classes in the
+     * compilation queue and it will find the class for that SourceUnit there then.
+     * method return a ClassNode instead of a SourceUnit, the res 
+     * @param name - the name of the class
+     * @param compilationUnit - the current CompilationUnit
+     * @return the LookupResult
+     */
+    public LookupResult resolveName(String name, CompilationUnit compilationUnit) {
+        ClassNode res = getFromClassCache(name);
+        if (res==NO_CLASS) return null;
+        if (res!=null) return new LookupResult(null,res);
+        LookupResult lr = findClassNode(name, compilationUnit);
+        if (lr != null) {
+            if (lr.isClassNode()) cacheClass(name, lr.getClassNode());
+            return lr;
+        } else {
+            cacheClass(name, NO_CLASS);
+            return null;
+        }
+    }
+    
+    /**
+     * caches a ClassNode
+     * @param name - the name of the class
+     * @param res - the ClassNode for that name
+     */
+    public void cacheClass(String name, ClassNode res) {
+        cachedClasses.put(name, res);
+    }
+    
+    /**
+     * returns whatever is stored in the class cache for the given name
+     * @param name - the name of the class
+     * @return the result of the lookup, which may be null
+     */
+    public ClassNode getFromClassCache(String name) {
+        // We use here the class cache cachedClasses to prevent
+        // calls to ClassLoader#loadClass. Disabling this cache will
+        // cause a major performance hit.
+        ClassNode cached = cachedClasses.get(name);
+        return cached;
+    }
+    
+    /**
+     * Extension point for custom lookup logic of finding ClassNodes. Per default
+     * this will use the CompilationUnit class loader to do a lookup on the class
+     * path and load the needed class using that loader. Or if a script is found 
+     * and that script is seen as "newer", the script will be used instead of the 
+     * class.
+     * 
+     * @param name - the name of the class
+     * @param compilationUnit - the current compilation unit
+     * @return the lookup result
+     */
+    public LookupResult findClassNode(String name, CompilationUnit compilationUnit) {
+        return tryAsLoaderClassOrScript(name, compilationUnit);
+    }
+
+    /**
+     * This method is used to realize the lookup of a class using the compilation
+     * unit class loader. Should no class be found we fall back to a script lookup.
+     * If a class is found we check if there is also a script and maybe use that
+     * one in case it is newer.<p/>
+     *
+     * Two class search strategies are possible: by ASM decompilation or by usual Java classloading.
+     * The latter is slower but is unavoidable for scripts executed in dynamic environments where
+     * the referenced classes might only be available in the classloader, not on disk.
+     */
+    private LookupResult tryAsLoaderClassOrScript(String name, CompilationUnit compilationUnit) {
+        GroovyClassLoader loader = compilationUnit.getClassLoader();
+
+        Map<String, Boolean> options = compilationUnit.configuration.getOptimizationOptions();
+        boolean useAsm = !Boolean.FALSE.equals(options.get("asmResolving"));
+        boolean useClassLoader = !Boolean.FALSE.equals(options.get("classLoaderResolving"));
+
+        LookupResult result = useAsm ? findDecompiled(name, compilationUnit, loader) : null;
+        if (result != null) {
+            return result;
+        }
+
+        if (!useClassLoader) {
+            return tryAsScript(name, compilationUnit, null);
+        }
+
+        return findByClassLoading(name, compilationUnit, loader);
+    }
+
+    /**
+     * Search for classes using class loading
+     */
+    private static LookupResult findByClassLoading(String name, CompilationUnit compilationUnit, GroovyClassLoader loader) {
+        Class cls;
+        try {
+            // NOTE: it's important to do no lookup against script files
+            // here since the GroovyClassLoader would create a new CompilationUnit
+            cls = loader.loadClass(name, false, true);
+        } catch (ClassNotFoundException cnfe) {
+            LookupResult lr = tryAsScript(name, compilationUnit, null);
+            return lr;
+        } catch (CompilationFailedException cfe) {
+            throw new GroovyBugError("The lookup for "+name+" caused a failed compilaton. There should not have been any compilation from this call.", cfe);
+        }
+        //TODO: the case of a NoClassDefFoundError needs a bit more research
+        // a simple recompilation is not possible it seems. The current class
+        // we are searching for is there, so we should mark that somehow.
+        // Basically the missing class needs to be completely compiled before
+        // we can again search for the current name.
+        /*catch (NoClassDefFoundError ncdfe) {
+            cachedClasses.put(name,SCRIPT);
+            return false;
+        }*/
+        if (cls == null) return null;
+        //NOTE: we might return false here even if we found a class,
+        //      because  we want to give a possible script a chance to
+        //      recompile. This can only be done if the loader was not
+        //      the instance defining the class.
+        ClassNode cn = ClassHelper.make(cls);
+        if (cls.getClassLoader() != loader) {
+            return tryAsScript(name, compilationUnit, cn);
+        }
+        return new LookupResult(null,cn);
+    }
+
+    /**
+     * Search for classes using ASM decompiler
+     */
+    private LookupResult findDecompiled(String name, CompilationUnit compilationUnit, GroovyClassLoader loader) {
+        ClassNode node = ClassHelper.make(name);
+        if (node.isResolved()) {
+            return new LookupResult(null, node);
+        }
+
+        DecompiledClassNode asmClass = null;
+        String fileName = name.replace('.', '/') + ".class";
+        URL resource = loader.getResource(fileName);
+        if (resource != null) {
+            try {
+                asmClass = new DecompiledClassNode(AsmDecompiler.parseClass(resource), new AsmReferenceResolver(this, compilationUnit));
+                if (!asmClass.getName().equals(name)) {
+                    // this may happen under Windows because getResource is case insensitive under that OS!
+                    asmClass = null;
+                }
+            } catch (IOException e) {
+                // fall through and attempt other search strategies
+            }
+        }
+
+        if (asmClass != null) {
+            if (isFromAnotherClassLoader(loader, fileName)) {
+                return tryAsScript(name, compilationUnit, asmClass);
+            }
+
+            return new LookupResult(null, asmClass);
+        }
+        return null;
+    }
+
+    private static boolean isFromAnotherClassLoader(GroovyClassLoader loader, String fileName) {
+        ClassLoader parent = loader.getParent();
+        return parent != null && parent.getResource(fileName) != null;
+    }
+
+    /**
+     * try to find a script using the compilation unit class loader.
+     */
+    private static LookupResult tryAsScript(String name, CompilationUnit compilationUnit, ClassNode oldClass) {
+        LookupResult lr = null;
+        if (oldClass!=null) {
+            lr = new LookupResult(null, oldClass);
+        }
+        
+        if (name.startsWith("java.")) return lr;
+        //TODO: don't ignore inner static classes completely
+        if (name.indexOf('$') != -1) return lr;
+        
+        // try to find a script from classpath*/
+        GroovyClassLoader gcl = compilationUnit.getClassLoader();
+        URL url = null;
+        try {
+            url = gcl.getResourceLoader().loadGroovySource(name);
+        } catch (MalformedURLException e) {
+            // fall through and let the URL be null
+        }
+        if (url != null && ( oldClass==null || isSourceNewer(url, oldClass))) {
+            SourceUnit su = compilationUnit.addSource(url);
+            return new LookupResult(su,null);
+        }
+        return lr;
+    }
+
+    /**
+     * get the time stamp of a class
+     * NOTE: copied from GroovyClassLoader
+     */
+    private static long getTimeStamp(ClassNode cls) {
+        if (!(cls instanceof DecompiledClassNode)) {
+            return Verifier.getTimestamp(cls.getTypeClass());
+        }
+
+        return ((DecompiledClassNode) cls).getCompilationTimeStamp();
+    }
+
+    /**
+     * returns true if the source in URL is newer than the class
+     * NOTE: copied from GroovyClassLoader
+     */
+    private static boolean isSourceNewer(URL source, ClassNode cls) {
+        try {
+            long lastMod;
+
+            // Special handling for file:// protocol, as getLastModified() often reports
+            // incorrect results (-1)
+            if (source.getProtocol().equals("file")) {
+                // Coerce the file URL to a File
+                String path = source.getPath().replace('/', File.separatorChar).replace('|', ':');
+                File file = new File(path);
+                lastMod = file.lastModified();
+            } else {
+                URLConnection conn = source.openConnection();
+                lastMod = conn.getLastModified();
+                conn.getInputStream().close();
+            }
+            return lastMod > getTimeStamp(cls);
+        } catch (IOException e) {
+            // if the stream can't be opened, let's keep the old reference
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/CompilationFailedException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/CompilationFailedException.java b/src/main/java/org/codehaus/groovy/control/CompilationFailedException.java
new file mode 100644
index 0000000..741b647
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/CompilationFailedException.java
@@ -0,0 +1,77 @@
+/*
+ *  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.GroovyRuntimeException;
+
+
+/**
+ * Thrown when compilation fails from source errors.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ */
+
+public class CompilationFailedException extends GroovyRuntimeException {
+
+    protected int phase;   // The phase in which the failures occurred
+    protected ProcessingUnit unit;    // The *Unit object this exception wraps
+
+    public CompilationFailedException(int phase, ProcessingUnit unit, Throwable cause) {
+        super(Phases.getDescription(phase) + " failed", cause);
+        this.phase = phase;
+        this.unit = unit;
+    }
+
+
+    public CompilationFailedException(int phase, ProcessingUnit unit) {
+        super(Phases.getDescription(phase) + " failed");
+        this.phase = phase;
+        this.unit = unit;
+    }
+
+
+    /**
+     * Formats the error data as a String.
+     */
+
+    /*public String toString() {
+        StringWriter data = new StringWriter();
+        PrintWriter writer = new PrintWriter(data);
+        Janitor janitor = new Janitor();
+
+        try {
+            unit.getErrorReporter().write(writer, janitor);
+        }
+        finally {
+            janitor.cleanup();
+        }
+
+        return data.toString();
+    }*/
+
+
+    /**
+     * Returns the ProcessingUnit in which the error occurred.
+     */
+
+    public ProcessingUnit getUnit() {
+        return this.unit;
+    }
+
+}