groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sun...@apache.org
Subject groovy git commit: Move source files to proper packages further
Date Wed, 20 Dec 2017 04:32:53 GMT
Repository: groovy
Updated Branches:
  refs/heads/master 6a7c42e8c -> a6c82ad08


Move source files to proper packages further


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/a6c82ad0
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/a6c82ad0
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/a6c82ad0

Branch: refs/heads/master
Commit: a6c82ad08b85ae8746b982a1a19107e93bfdb568
Parents: 6a7c42e
Author: sunlan <sunlan@apache.org>
Authored: Wed Dec 20 12:32:48 2017 +0800
Committer: sunlan <sunlan@apache.org>
Committed: Wed Dec 20 12:32:48 2017 +0800

----------------------------------------------------------------------
 .../transform/ASTTestTransformation.groovy      | 233 --------------
 ...itionalInterruptibleASTTransformation.groovy | 145 ---------
 .../ThreadInterruptibleASTTransformation.groovy |  98 ------
 .../TimedInterruptibleASTTransformation.groovy  | 321 -------------------
 .../transform/ASTTestTransformation.groovy      | 233 ++++++++++++++
 ...itionalInterruptibleASTTransformation.groovy | 145 +++++++++
 .../ThreadInterruptibleASTTransformation.groovy |  98 ++++++
 .../TimedInterruptibleASTTransformation.groovy  | 321 +++++++++++++++++++
 8 files changed, 797 insertions(+), 797 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/a6c82ad0/src/main/groovy/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy b/src/main/groovy/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
deleted file mode 100644
index dcfe314..0000000
--- a/src/main/groovy/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- *  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.transform
-
-import groovy.transform.CompilationUnitAware
-import org.codehaus.groovy.ast.ASTNode
-import org.codehaus.groovy.ast.AnnotationNode
-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.ClosureExpression
-import org.codehaus.groovy.ast.expr.PropertyExpression
-import org.codehaus.groovy.ast.expr.VariableExpression
-import org.codehaus.groovy.ast.stmt.Statement
-import org.codehaus.groovy.control.CompilationUnit
-import org.codehaus.groovy.control.CompilePhase
-import org.codehaus.groovy.control.CompilerConfiguration
-import org.codehaus.groovy.control.ErrorCollector
-import org.codehaus.groovy.control.Janitor
-import org.codehaus.groovy.control.ProcessingUnit
-import org.codehaus.groovy.control.SourceUnit
-import org.codehaus.groovy.control.customizers.ImportCustomizer
-import org.codehaus.groovy.control.io.ReaderSource
-import org.codehaus.groovy.runtime.MethodClosure
-import org.codehaus.groovy.syntax.SyntaxException
-import org.codehaus.groovy.tools.Utilities
-
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
-
-@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
-class ASTTestTransformation extends AbstractASTTransformation implements CompilationUnitAware {
-    private CompilationUnit compilationUnit
-
-    void visit(final ASTNode[] nodes, final SourceUnit source) {
-        AnnotationNode annotationNode = nodes[0]
-        def member = annotationNode.getMember('phase')
-        def phase = null
-        if (member) {
-            if (member instanceof VariableExpression) {
-                phase = CompilePhase.valueOf(member.text)
-            } else if (member instanceof PropertyExpression) {
-                phase = CompilePhase.valueOf(member.propertyAsString)
-            }
-            annotationNode.setMember('phase', propX(classX(ClassHelper.make(CompilePhase)), phase.toString()))
-        }
-        member = annotationNode.getMember('value')
-        if (member && !(member instanceof ClosureExpression)) {
-            throw new SyntaxException("ASTTest value must be a closure", member.getLineNumber(), member.getColumnNumber())
-        }
-        if (!member && !annotationNode.getNodeMetaData(ASTTestTransformation)) {
-            throw new SyntaxException("Missing test expression", annotationNode.getLineNumber(), annotationNode.getColumnNumber())
-        }
-        // convert value into node metadata so that the expression doesn't mix up with other AST xforms like type checking
-        annotationNode.putNodeMetaData(ASTTestTransformation, member)
-        annotationNode.getMembers().remove('value')
-
-        def pcallback = compilationUnit.progressCallback
-        def callback = new CompilationUnit.ProgressCallback() {
-            Binding binding = new Binding([:].withDefault {null})
-
-            @Override
-            void call(final ProcessingUnit context, final int phaseRef) {
-                if (phase==null ||  phaseRef == phase.phaseNumber) {
-                    ClosureExpression testClosure = nodes[0].getNodeMetaData(ASTTestTransformation)
-                    StringBuilder sb = new StringBuilder()
-                    for (int i = testClosure.lineNumber; i <= testClosure.lastLineNumber; i++) {
-                        sb.append(source.source.getLine(i, new Janitor())).append('\n')
-                    }
-                    def testSource = sb.substring(testClosure.columnNumber + 1, sb.length())
-                    testSource = testSource.substring(0, testSource.lastIndexOf('}'))
-                    CompilerConfiguration config = new CompilerConfiguration()
-                    def customizer = new ImportCustomizer()
-                    config.addCompilationCustomizers(customizer)
-                    binding['sourceUnit'] = source
-                    binding['node'] = nodes[1]
-                    binding['lookup'] = new MethodClosure(LabelFinder, "lookup").curry(nodes[1])
-                    binding['compilationUnit'] = compilationUnit
-                    binding['compilePhase'] = CompilePhase.fromPhaseNumber(phaseRef)
-
-                    GroovyShell shell = new GroovyShell(binding, config)
-
-                    source.AST.imports.each {
-                        customizer.addImport(it.alias, it.type.name)
-                    }
-                    source.AST.starImports.each {
-                        customizer.addStarImports(it.packageName)
-                    }
-                    source.AST.staticImports.each {
-                        customizer.addStaticImport(it.value.alias, it.value.type.name, it.value.fieldName)
-                    }
-                    source.AST.staticStarImports.each {
-                        customizer.addStaticStars(it.value.className)
-                    }
-                    shell.evaluate(testSource)
-                }
-            }
-        }
-        
-        if (pcallback!=null) {
-            if (pcallback instanceof ProgressCallbackChain) {
-                pcallback.addCallback(callback)                
-            } else {
-                pcallback = new ProgressCallbackChain(pcallback, callback)
-            }
-            callback = pcallback
-        }
-        
-        compilationUnit.setProgressCallback(callback)
-
-    }
-
-    void setCompilationUnit(final CompilationUnit unit) {
-        this.compilationUnit = unit
-    }
-
-    private static class AssertionSourceDelegatingSourceUnit extends SourceUnit {
-        private final ReaderSource delegate
-
-        AssertionSourceDelegatingSourceUnit(final String name, final ReaderSource source, final CompilerConfiguration flags, final GroovyClassLoader loader, final ErrorCollector er) {
-            super(name, '', flags, loader, er)
-            delegate = source
-        }
-
-        @Override
-        String getSample(final int line, final int column, final Janitor janitor) {
-            String sample = null;
-            String text = delegate.getLine(line, janitor);
-
-            if (text != null) {
-                if (column > 0) {
-                    String marker = Utilities.repeatString(" ", column - 1) + "^";
-
-                    if (column > 40) {
-                        int start = column - 30 - 1;
-                        int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
-                        sample = "   " + text.substring(start, end) + Utilities.eol() + "   " +
-                                marker.substring(start, marker.length());
-                    } else {
-                        sample = "   " + text + Utilities.eol() + "   " + marker;
-                    }
-                } else {
-                    sample = text;
-                }
-            }
-
-            return sample;
-
-        }
-
-    }
-    
-    private static class ProgressCallbackChain extends CompilationUnit.ProgressCallback {
-
-        private final List<CompilationUnit.ProgressCallback> chain = new LinkedList<CompilationUnit.ProgressCallback>()
-
-        ProgressCallbackChain(CompilationUnit.ProgressCallback... callbacks) {
-            if (callbacks!=null) {
-                callbacks.each { addCallback(it) }
-            }
-        }
-
-        public void addCallback(CompilationUnit.ProgressCallback callback) {
-            chain << callback
-        }
-        
-        @Override
-        void call(final ProcessingUnit context, final int phase) {
-            chain*.call(context, phase)
-        }
-    }
-
-    public static class LabelFinder extends ClassCodeVisitorSupport {
-
-        public static List<Statement> lookup(MethodNode node, String label) {
-            LabelFinder finder = new LabelFinder(label, null)
-            node.code.visit(finder)
-
-            finder.targets
-        }
-
-        public static List<Statement> lookup(ClassNode node, String label) {
-            LabelFinder finder = new LabelFinder(label, null)
-            node.methods*.code*.visit(finder)
-            node.declaredConstructors*.code*.visit(finder)
-
-            finder.targets
-        }
-
-        private final String label
-        private final SourceUnit unit
-
-        private final List<Statement> targets = new LinkedList<Statement>();
-
-        LabelFinder(final String label, final SourceUnit unit) {
-            this.label = label
-            this.unit = unit;
-        }
-
-        @Override
-        protected SourceUnit getSourceUnit() {
-            unit
-        }
-
-        @Override
-        protected void visitStatement(final Statement statement) {
-            super.visitStatement(statement)
-            if (statement.statementLabel==label) targets << statement
-        }
-
-        List<Statement> getTargets() {
-            return Collections.unmodifiableList(targets)
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/groovy/blob/a6c82ad0/src/main/groovy/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy b/src/main/groovy/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy
deleted file mode 100644
index 2cda121..0000000
--- a/src/main/groovy/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- *  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.transform
-
-import groovy.transform.ConditionalInterrupt
-import org.codehaus.groovy.ast.AnnotatedNode
-import org.codehaus.groovy.ast.AnnotationNode
-import org.codehaus.groovy.ast.ClassHelper
-import org.codehaus.groovy.ast.ClassNode
-import org.codehaus.groovy.ast.FieldNode
-import org.codehaus.groovy.ast.MethodNode
-import org.codehaus.groovy.ast.Parameter
-import org.codehaus.groovy.ast.PropertyNode
-import org.codehaus.groovy.ast.expr.ArgumentListExpression
-import org.codehaus.groovy.ast.expr.ClosureExpression
-import org.codehaus.groovy.ast.expr.Expression
-import org.codehaus.groovy.ast.expr.MethodCallExpression
-import org.codehaus.groovy.ast.expr.VariableExpression
-import org.codehaus.groovy.ast.tools.ClosureUtils
-import org.codehaus.groovy.control.CompilePhase
-
-/**
- * Allows "interrupt-safe" executions of scripts by adding a custom conditional
- * check on loops (for, while, do) and first statement of closures. By default, also adds an interrupt check
- * statement on the beginning of method calls.
- *
- * @see groovy.transform.ConditionalInterrupt
- * @author Cedric Champeau
- * @author Hamlet D'Arcy
- * @author Paul King
- * @since 1.8.0
- */
-@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
-public class ConditionalInterruptibleASTTransformation extends AbstractInterruptibleASTTransformation {
-
-  private static final ClassNode MY_TYPE = ClassHelper.make(ConditionalInterrupt)
-
-  private ClosureExpression conditionNode
-  private String conditionMethod
-  private MethodCallExpression conditionCallExpression
-  private ClassNode currentClass
-
-  protected ClassNode type() {
-    return MY_TYPE
-  }
-
-  protected void setupTransform(AnnotationNode node) {
-    super.setupTransform(node)
-    def member = node.getMember("value")
-    if (!member || !(member instanceof ClosureExpression)) internalError("Expected closure value for annotation parameter 'value'. Found $member")
-    conditionNode = member;
-    conditionMethod = 'conditionalTransform' + node.hashCode() + '$condition'
-    conditionCallExpression = new MethodCallExpression(new VariableExpression('this'), conditionMethod, new ArgumentListExpression())
-  }
-
-  protected String getErrorMessage() {
-    'Execution interrupted. The following condition failed: ' + convertClosureToSource(conditionNode)
-  }
-
-  void visitClass(ClassNode type) {
-    currentClass = type
-    def method = type.addMethod(conditionMethod, ACC_PRIVATE | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, conditionNode.code)
-    method.synthetic = true
-    if (applyToAllMembers) {
-      super.visitClass(type)
-    }
-  }
-
-  protected Expression createCondition() {
-    conditionCallExpression
-  }
-
-  @Override
-  void visitAnnotations(AnnotatedNode node) {
-    // this transformation does not apply on annotation nodes
-    // visiting could lead to stack overflows
-  }
-
-  @Override
-  void visitField(FieldNode node) {
-    if (!node.isStatic() && !node.isSynthetic()) {
-      super.visitField node
-    }
-  }
-
-  @Override
-  void visitProperty(PropertyNode node) {
-    if (!node.isStatic() && !node.isSynthetic()) {
-      super.visitProperty node
-    }
-  }
-
-  @Override
-  void visitClosureExpression(ClosureExpression closureExpr) {
-    if (closureExpr == conditionNode) return // do not visit the closure from the annotation itself
-    def code = closureExpr.code
-    closureExpr.code = wrapBlock(code)
-    super.visitClosureExpression closureExpr
-  }
-
-  @Override
-  void visitMethod(MethodNode node) {
-    if (node.name == conditionMethod && !node.isSynthetic()) return // do not visit the generated method
-    if (node.name == 'run' && currentClass.isScript() && node.parameters.length == 0) {
-      // the run() method should not have the statement added, otherwise the script binding won't be set before
-      // the condition is actually tested
-      super.visitMethod(node)
-    } else {
-      if (checkOnMethodStart && !node.isSynthetic() && !node.isStatic() && !node.isAbstract()) {
-        def code = node.code
-        node.code = wrapBlock(code);
-      }
-      if (!node.isSynthetic() && !node.isStatic()) super.visitMethod(node)
-    }
-  }
-
-  /**
-   * Converts a ClosureExpression into the String source.
-   * @param expression a closure
-   * @return the source the closure was created from
-   */
-  private String convertClosureToSource(ClosureExpression expression) {
-    try {
-        return ClosureUtils.convertClosureToSource(this.source.source, expression);
-    } catch(Exception e) {
-        return e.message
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/groovy/blob/a6c82ad0/src/main/groovy/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy b/src/main/groovy/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy
deleted file mode 100644
index a4fb4c3..0000000
--- a/src/main/groovy/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- *  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.transform
-
-import groovy.transform.CompileStatic
-import groovy.transform.ThreadInterrupt
-import org.codehaus.groovy.ast.ClassHelper
-import org.codehaus.groovy.ast.ClassNode
-import org.codehaus.groovy.ast.MethodNode
-import org.codehaus.groovy.ast.Parameter
-import org.codehaus.groovy.ast.expr.ArgumentListExpression
-import org.codehaus.groovy.ast.expr.ClassExpression
-import org.codehaus.groovy.ast.expr.ClosureExpression
-import org.codehaus.groovy.ast.expr.Expression
-import org.codehaus.groovy.ast.expr.MethodCallExpression
-import org.codehaus.groovy.control.CompilePhase
-
-/**
- * Allows "interrupt-safe" executions of scripts by adding Thread.currentThread().isInterrupted()
- * checks on loops (for, while, do) and first statement of closures. By default, also adds an interrupt check
- * statement on the beginning of method calls.
- *
- * @see groovy.transform.ThreadInterrupt
- *
- * @author Cedric Champeau
- * @author Hamlet D'Arcy
- *
- * @since 1.8.0
- */
-@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
-@CompileStatic
-public class ThreadInterruptibleASTTransformation extends AbstractInterruptibleASTTransformation {
-
-    private static final ClassNode MY_TYPE = ClassHelper.make(ThreadInterrupt)
-    private static final ClassNode THREAD_TYPE = ClassHelper.make(Thread)
-    private static final MethodNode CURRENTTHREAD_METHOD
-    private static final MethodNode ISINTERRUPTED_METHOD
-
-    static {
-        CURRENTTHREAD_METHOD = THREAD_TYPE.getMethod('currentThread', Parameter.EMPTY_ARRAY)
-        ISINTERRUPTED_METHOD = THREAD_TYPE.getMethod('isInterrupted', Parameter.EMPTY_ARRAY)
-    }
-
-    protected ClassNode type() {
-        return MY_TYPE;
-    }
-
-    protected String getErrorMessage() {
-        'Execution interrupted. The current thread has been interrupted.'
-    }
-
-    protected Expression createCondition() {
-        def currentThread = new MethodCallExpression(new ClassExpression(THREAD_TYPE),
-                'currentThread',
-                ArgumentListExpression.EMPTY_ARGUMENTS)
-        currentThread.methodTarget = CURRENTTHREAD_METHOD
-        def isInterrupted = new MethodCallExpression(
-                currentThread,
-                'isInterrupted', ArgumentListExpression.EMPTY_ARGUMENTS)
-        isInterrupted.methodTarget = ISINTERRUPTED_METHOD
-        [currentThread, isInterrupted]*.implicitThis = false
-
-        isInterrupted
-    }
-
-
-    @Override
-    public void visitClosureExpression(ClosureExpression closureExpr) {
-        def code = closureExpr.code
-        closureExpr.code = wrapBlock(code)
-        super.visitClosureExpression closureExpr
-    }
-
-    @Override
-    public void visitMethod(MethodNode node) {
-        if (checkOnMethodStart && !node.isSynthetic() && !node.isAbstract()) {
-            def code = node.code
-            node.code = wrapBlock(code);
-        }
-        super.visitMethod(node)
-    }
-}

http://git-wip-us.apache.org/repos/asf/groovy/blob/a6c82ad0/src/main/groovy/groovy/org/codehaus/groovy/transform/TimedInterruptibleASTTransformation.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/org/codehaus/groovy/transform/TimedInterruptibleASTTransformation.groovy b/src/main/groovy/groovy/org/codehaus/groovy/transform/TimedInterruptibleASTTransformation.groovy
deleted file mode 100644
index fbc923b..0000000
--- a/src/main/groovy/groovy/org/codehaus/groovy/transform/TimedInterruptibleASTTransformation.groovy
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- *  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.transform
-
-import groovy.transform.TimedInterrupt
-import org.codehaus.groovy.ast.ASTNode
-import org.codehaus.groovy.ast.AnnotatedNode
-import org.codehaus.groovy.ast.AnnotationNode
-import org.codehaus.groovy.ast.ClassCodeVisitorSupport
-import org.codehaus.groovy.ast.ClassHelper
-import org.codehaus.groovy.ast.ClassNode
-import org.codehaus.groovy.ast.FieldNode
-import org.codehaus.groovy.ast.MethodNode
-import org.codehaus.groovy.ast.PropertyNode
-import org.codehaus.groovy.ast.expr.ClosureExpression
-import org.codehaus.groovy.ast.expr.ConstantExpression
-import org.codehaus.groovy.ast.expr.DeclarationExpression
-import org.codehaus.groovy.ast.expr.Expression
-import org.codehaus.groovy.ast.stmt.BlockStatement
-import org.codehaus.groovy.ast.stmt.DoWhileStatement
-import org.codehaus.groovy.ast.stmt.ForStatement
-import org.codehaus.groovy.ast.stmt.WhileStatement
-import org.codehaus.groovy.control.CompilePhase
-import org.codehaus.groovy.control.SourceUnit
-
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.TimeoutException
-
-import static org.codehaus.groovy.ast.ClassHelper.make
-import static org.codehaus.groovy.ast.tools.GeneralUtils.args
-import static org.codehaus.groovy.ast.tools.GeneralUtils.callX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.constX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ltX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.plusX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS
-import static org.codehaus.groovy.ast.tools.GeneralUtils.varX
-
-/**
- * Allows "interrupt-safe" executions of scripts by adding timer expiration
- * checks on loops (for, while, do) and first statement of closures. By default,
- * also adds an interrupt check statement on the beginning of method calls.
- *
- * @author Cedric Champeau
- * @author Hamlet D'Arcy
- * @author Paul King
- * @see groovy.transform.ThreadInterrupt
- * @since 1.8.0
- */
-@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
-public class TimedInterruptibleASTTransformation extends AbstractASTTransformation {
-
-    private static final ClassNode MY_TYPE = make(TimedInterrupt)
-    private static final String CHECK_METHOD_START_MEMBER = 'checkOnMethodStart'
-    private static final String APPLY_TO_ALL_CLASSES = 'applyToAllClasses'
-    private static final String APPLY_TO_ALL_MEMBERS = 'applyToAllMembers'
-    private static final String THROWN_EXCEPTION_TYPE = "thrown"
-
-    public void visit(ASTNode[] nodes, SourceUnit source) {
-        init(nodes, source);
-        AnnotationNode node = nodes[0]
-        AnnotatedNode annotatedNode = nodes[1]
-        if (!MY_TYPE.equals(node.getClassNode())) {
-            internalError("Transformation called from wrong annotation: $node.classNode.name")
-        }
-
-        def checkOnMethodStart = getConstantAnnotationParameter(node, CHECK_METHOD_START_MEMBER, Boolean.TYPE, true)
-        def applyToAllMembers = getConstantAnnotationParameter(node, APPLY_TO_ALL_MEMBERS, Boolean.TYPE, true)
-        def applyToAllClasses = applyToAllMembers ? getConstantAnnotationParameter(node, APPLY_TO_ALL_CLASSES, Boolean.TYPE, true) : false
-        def maximum = getConstantAnnotationParameter(node, 'value', Long.TYPE, Long.MAX_VALUE)
-        def thrown = AbstractInterruptibleASTTransformation.getClassAnnotationParameter(node, THROWN_EXCEPTION_TYPE, make(TimeoutException))
-
-        Expression unit = node.getMember('unit') ?: propX(classX(TimeUnit), "SECONDS")
-
-        // should be limited to the current SourceUnit or propagated to the whole CompilationUnit
-        // DO NOT inline visitor creation in code below. It has state that must not persist between calls
-        if (applyToAllClasses) {
-            // guard every class and method defined in this script
-            source.getAST()?.classes?.each { ClassNode it ->
-                def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
-                visitor.visitClass(it)
-            }
-        } else if (annotatedNode instanceof ClassNode) {
-            // only guard this particular class
-            def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
-            visitor.visitClass annotatedNode
-        } else if (!applyToAllMembers && annotatedNode instanceof MethodNode) {
-            // only guard this particular method (plus initCode for class)
-            def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
-            visitor.visitMethod annotatedNode
-            visitor.visitClass annotatedNode.declaringClass
-        } else if (!applyToAllMembers && annotatedNode instanceof FieldNode) {
-            // only guard this particular field (plus initCode for class)
-            def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
-            visitor.visitField annotatedNode
-            visitor.visitClass annotatedNode.declaringClass
-        } else if (!applyToAllMembers && annotatedNode instanceof DeclarationExpression) {
-            // only guard this particular declaration (plus initCode for class)
-            def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
-            visitor.visitDeclarationExpression annotatedNode
-            visitor.visitClass annotatedNode.declaringClass
-        } else {
-            // only guard the script class
-            source.getAST()?.classes?.each { ClassNode it ->
-                if (it.isScript()) {
-                    def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
-                    visitor.visitClass(it)
-                }
-            }
-        }
-    }
-
-    static def getConstantAnnotationParameter(AnnotationNode node, String parameterName, Class type, defaultValue) {
-        def member = node.getMember(parameterName)
-        if (member) {
-            if (member instanceof ConstantExpression) {
-                // TODO not sure this try offers value - testing Groovy annotation type handing - throw GroovyBugError or remove?
-                try {
-                    return member.value.asType(type)
-                } catch (ignore) {
-                    internalError("Expecting boolean value for ${parameterName} annotation parameter. Found $member")
-                }
-            } else {
-                internalError("Expecting boolean value for ${parameterName} annotation parameter. Found $member")
-            }
-        }
-        return defaultValue
-    }
-
-    private static void internalError(String message) {
-        throw new RuntimeException("Internal error: $message")
-    }
-
-    private static class TimedInterruptionVisitor extends ClassCodeVisitorSupport {
-        final private SourceUnit source
-        final private boolean checkOnMethodStart
-        final private boolean applyToAllClasses
-        final private boolean applyToAllMembers
-        private FieldNode expireTimeField = null
-        private FieldNode startTimeField = null
-        private final Expression unit
-        private final maximum
-        private final ClassNode thrown
-        private final String basename
-
-        TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, hash) {
-            this.source = source
-            this.checkOnMethodStart = checkOnMethodStart
-            this.applyToAllClasses = applyToAllClasses
-            this.applyToAllMembers = applyToAllMembers
-            this.unit = unit
-            this.maximum = maximum
-            this.thrown = thrown
-            this.basename = 'timedInterrupt' + hash
-        }
-
-        /**
-         * @return Returns the interruption check statement.
-         */
-        final createInterruptStatement() {
-            ifS(
-
-                    ltX(
-                            propX(varX("this"), basename + '$expireTime'),
-                            callX(make(System), 'nanoTime')
-                    ),
-                    throwS(
-                            ctorX(thrown,
-                                    args(
-                                            plusX(
-                                                    plusX(
-                                                            constX('Execution timed out after ' + maximum + ' '),
-                                                            callX(callX(unit, 'name'), 'toLowerCase', propX(classX(Locale), 'US'))
-                                                    ),
-                                                    plusX(
-                                                            constX('. Start time: '),
-                                                            propX(varX("this"), basename + '$startTime')
-                                                    )
-                                            )
-
-                                    )
-                            )
-                    )
-            )
-        }
-
-        /**
-         * Takes a statement and wraps it into a block statement which first element is the interruption check statement.
-         * @param statement the statement to be wrapped
-         * @return a {@link BlockStatement block statement}    which first element is for checking interruption, and the
-         * second one the statement to be wrapped.
-         */
-        private wrapBlock(statement) {
-            def stmt = new BlockStatement();
-            stmt.addStatement(createInterruptStatement());
-            stmt.addStatement(statement);
-            stmt
-        }
-
-        @Override
-        void visitClass(ClassNode node) {
-            if (node.getDeclaredField(basename + '$expireTime')) {
-                return
-            }
-            expireTimeField = node.addField(basename + '$expireTime',
-                    ACC_FINAL | ACC_PRIVATE,
-                    ClassHelper.long_TYPE,
-                    plusX(
-                            callX(make(System), 'nanoTime'),
-                            callX(
-                                    propX(classX(TimeUnit), 'NANOSECONDS'),
-                                    'convert',
-                                    args(constX(maximum, true), unit)
-                            )
-                    )
-            );
-            expireTimeField.synthetic = true
-            startTimeField = node.addField(basename + '$startTime',
-                    ACC_FINAL | ACC_PRIVATE,
-                    make(Date),
-                    ctorX(make(Date))
-            )
-            startTimeField.synthetic = true
-
-            // force these fields to be initialized first
-            node.fields.remove(expireTimeField)
-            node.fields.remove(startTimeField)
-            node.fields.add(0, startTimeField)
-            node.fields.add(0, expireTimeField)
-            if (applyToAllMembers) {
-                super.visitClass node
-            }
-        }
-
-        @Override
-        void visitClosureExpression(ClosureExpression closureExpr) {
-            def code = closureExpr.code
-            if (code instanceof BlockStatement) {
-                code.statements.add(0, createInterruptStatement())
-            } else {
-                closureExpr.code = wrapBlock(code)
-            }
-            super.visitClosureExpression closureExpr
-        }
-
-        @Override
-        void visitField(FieldNode node) {
-            if (!node.isStatic() && !node.isSynthetic()) {
-                super.visitField node
-            }
-        }
-
-        @Override
-        void visitProperty(PropertyNode node) {
-            if (!node.isStatic() && !node.isSynthetic()) {
-                super.visitProperty node
-            }
-        }
-
-        /**
-         * Shortcut method which avoids duplicating code for every type of loop.
-         * Actually wraps the loopBlock of different types of loop statements.
-         */
-        private visitLoop(loopStatement) {
-            def statement = loopStatement.loopBlock
-            loopStatement.loopBlock = wrapBlock(statement)
-        }
-
-        @Override
-        void visitForLoop(ForStatement forStatement) {
-            visitLoop(forStatement)
-            super.visitForLoop(forStatement)
-        }
-
-        @Override
-        void visitDoWhileLoop(final DoWhileStatement doWhileStatement) {
-            visitLoop(doWhileStatement)
-            super.visitDoWhileLoop(doWhileStatement)
-        }
-
-        @Override
-        void visitWhileLoop(final WhileStatement whileStatement) {
-            visitLoop(whileStatement)
-            super.visitWhileLoop(whileStatement)
-        }
-
-        @Override
-        void visitMethod(MethodNode node) {
-            if (checkOnMethodStart && !node.isSynthetic() && !node.isStatic() && !node.isAbstract()) {
-                def code = node.code
-                node.code = wrapBlock(code);
-            }
-            if (!node.isSynthetic() && !node.isStatic()) {
-                super.visitMethod(node)
-            }
-        }
-
-        protected SourceUnit getSourceUnit() {
-            return source;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/groovy/blob/a6c82ad0/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy b/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
new file mode 100644
index 0000000..dcfe314
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
@@ -0,0 +1,233 @@
+/*
+ *  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.transform
+
+import groovy.transform.CompilationUnitAware
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.ast.AnnotationNode
+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.ClosureExpression
+import org.codehaus.groovy.ast.expr.PropertyExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.ast.stmt.Statement
+import org.codehaus.groovy.control.CompilationUnit
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.ErrorCollector
+import org.codehaus.groovy.control.Janitor
+import org.codehaus.groovy.control.ProcessingUnit
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.codehaus.groovy.control.io.ReaderSource
+import org.codehaus.groovy.runtime.MethodClosure
+import org.codehaus.groovy.syntax.SyntaxException
+import org.codehaus.groovy.tools.Utilities
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
+
+@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+class ASTTestTransformation extends AbstractASTTransformation implements CompilationUnitAware {
+    private CompilationUnit compilationUnit
+
+    void visit(final ASTNode[] nodes, final SourceUnit source) {
+        AnnotationNode annotationNode = nodes[0]
+        def member = annotationNode.getMember('phase')
+        def phase = null
+        if (member) {
+            if (member instanceof VariableExpression) {
+                phase = CompilePhase.valueOf(member.text)
+            } else if (member instanceof PropertyExpression) {
+                phase = CompilePhase.valueOf(member.propertyAsString)
+            }
+            annotationNode.setMember('phase', propX(classX(ClassHelper.make(CompilePhase)), phase.toString()))
+        }
+        member = annotationNode.getMember('value')
+        if (member && !(member instanceof ClosureExpression)) {
+            throw new SyntaxException("ASTTest value must be a closure", member.getLineNumber(), member.getColumnNumber())
+        }
+        if (!member && !annotationNode.getNodeMetaData(ASTTestTransformation)) {
+            throw new SyntaxException("Missing test expression", annotationNode.getLineNumber(), annotationNode.getColumnNumber())
+        }
+        // convert value into node metadata so that the expression doesn't mix up with other AST xforms like type checking
+        annotationNode.putNodeMetaData(ASTTestTransformation, member)
+        annotationNode.getMembers().remove('value')
+
+        def pcallback = compilationUnit.progressCallback
+        def callback = new CompilationUnit.ProgressCallback() {
+            Binding binding = new Binding([:].withDefault {null})
+
+            @Override
+            void call(final ProcessingUnit context, final int phaseRef) {
+                if (phase==null ||  phaseRef == phase.phaseNumber) {
+                    ClosureExpression testClosure = nodes[0].getNodeMetaData(ASTTestTransformation)
+                    StringBuilder sb = new StringBuilder()
+                    for (int i = testClosure.lineNumber; i <= testClosure.lastLineNumber; i++) {
+                        sb.append(source.source.getLine(i, new Janitor())).append('\n')
+                    }
+                    def testSource = sb.substring(testClosure.columnNumber + 1, sb.length())
+                    testSource = testSource.substring(0, testSource.lastIndexOf('}'))
+                    CompilerConfiguration config = new CompilerConfiguration()
+                    def customizer = new ImportCustomizer()
+                    config.addCompilationCustomizers(customizer)
+                    binding['sourceUnit'] = source
+                    binding['node'] = nodes[1]
+                    binding['lookup'] = new MethodClosure(LabelFinder, "lookup").curry(nodes[1])
+                    binding['compilationUnit'] = compilationUnit
+                    binding['compilePhase'] = CompilePhase.fromPhaseNumber(phaseRef)
+
+                    GroovyShell shell = new GroovyShell(binding, config)
+
+                    source.AST.imports.each {
+                        customizer.addImport(it.alias, it.type.name)
+                    }
+                    source.AST.starImports.each {
+                        customizer.addStarImports(it.packageName)
+                    }
+                    source.AST.staticImports.each {
+                        customizer.addStaticImport(it.value.alias, it.value.type.name, it.value.fieldName)
+                    }
+                    source.AST.staticStarImports.each {
+                        customizer.addStaticStars(it.value.className)
+                    }
+                    shell.evaluate(testSource)
+                }
+            }
+        }
+        
+        if (pcallback!=null) {
+            if (pcallback instanceof ProgressCallbackChain) {
+                pcallback.addCallback(callback)                
+            } else {
+                pcallback = new ProgressCallbackChain(pcallback, callback)
+            }
+            callback = pcallback
+        }
+        
+        compilationUnit.setProgressCallback(callback)
+
+    }
+
+    void setCompilationUnit(final CompilationUnit unit) {
+        this.compilationUnit = unit
+    }
+
+    private static class AssertionSourceDelegatingSourceUnit extends SourceUnit {
+        private final ReaderSource delegate
+
+        AssertionSourceDelegatingSourceUnit(final String name, final ReaderSource source, final CompilerConfiguration flags, final GroovyClassLoader loader, final ErrorCollector er) {
+            super(name, '', flags, loader, er)
+            delegate = source
+        }
+
+        @Override
+        String getSample(final int line, final int column, final Janitor janitor) {
+            String sample = null;
+            String text = delegate.getLine(line, janitor);
+
+            if (text != null) {
+                if (column > 0) {
+                    String marker = Utilities.repeatString(" ", column - 1) + "^";
+
+                    if (column > 40) {
+                        int start = column - 30 - 1;
+                        int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
+                        sample = "   " + text.substring(start, end) + Utilities.eol() + "   " +
+                                marker.substring(start, marker.length());
+                    } else {
+                        sample = "   " + text + Utilities.eol() + "   " + marker;
+                    }
+                } else {
+                    sample = text;
+                }
+            }
+
+            return sample;
+
+        }
+
+    }
+    
+    private static class ProgressCallbackChain extends CompilationUnit.ProgressCallback {
+
+        private final List<CompilationUnit.ProgressCallback> chain = new LinkedList<CompilationUnit.ProgressCallback>()
+
+        ProgressCallbackChain(CompilationUnit.ProgressCallback... callbacks) {
+            if (callbacks!=null) {
+                callbacks.each { addCallback(it) }
+            }
+        }
+
+        public void addCallback(CompilationUnit.ProgressCallback callback) {
+            chain << callback
+        }
+        
+        @Override
+        void call(final ProcessingUnit context, final int phase) {
+            chain*.call(context, phase)
+        }
+    }
+
+    public static class LabelFinder extends ClassCodeVisitorSupport {
+
+        public static List<Statement> lookup(MethodNode node, String label) {
+            LabelFinder finder = new LabelFinder(label, null)
+            node.code.visit(finder)
+
+            finder.targets
+        }
+
+        public static List<Statement> lookup(ClassNode node, String label) {
+            LabelFinder finder = new LabelFinder(label, null)
+            node.methods*.code*.visit(finder)
+            node.declaredConstructors*.code*.visit(finder)
+
+            finder.targets
+        }
+
+        private final String label
+        private final SourceUnit unit
+
+        private final List<Statement> targets = new LinkedList<Statement>();
+
+        LabelFinder(final String label, final SourceUnit unit) {
+            this.label = label
+            this.unit = unit;
+        }
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            unit
+        }
+
+        @Override
+        protected void visitStatement(final Statement statement) {
+            super.visitStatement(statement)
+            if (statement.statementLabel==label) targets << statement
+        }
+
+        List<Statement> getTargets() {
+            return Collections.unmodifiableList(targets)
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/a6c82ad0/src/main/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy b/src/main/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy
new file mode 100644
index 0000000..2cda121
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy
@@ -0,0 +1,145 @@
+/*
+ *  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.transform
+
+import groovy.transform.ConditionalInterrupt
+import org.codehaus.groovy.ast.AnnotatedNode
+import org.codehaus.groovy.ast.AnnotationNode
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.FieldNode
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.Parameter
+import org.codehaus.groovy.ast.PropertyNode
+import org.codehaus.groovy.ast.expr.ArgumentListExpression
+import org.codehaus.groovy.ast.expr.ClosureExpression
+import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.ast.tools.ClosureUtils
+import org.codehaus.groovy.control.CompilePhase
+
+/**
+ * Allows "interrupt-safe" executions of scripts by adding a custom conditional
+ * check on loops (for, while, do) and first statement of closures. By default, also adds an interrupt check
+ * statement on the beginning of method calls.
+ *
+ * @see groovy.transform.ConditionalInterrupt
+ * @author Cedric Champeau
+ * @author Hamlet D'Arcy
+ * @author Paul King
+ * @since 1.8.0
+ */
+@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
+public class ConditionalInterruptibleASTTransformation extends AbstractInterruptibleASTTransformation {
+
+  private static final ClassNode MY_TYPE = ClassHelper.make(ConditionalInterrupt)
+
+  private ClosureExpression conditionNode
+  private String conditionMethod
+  private MethodCallExpression conditionCallExpression
+  private ClassNode currentClass
+
+  protected ClassNode type() {
+    return MY_TYPE
+  }
+
+  protected void setupTransform(AnnotationNode node) {
+    super.setupTransform(node)
+    def member = node.getMember("value")
+    if (!member || !(member instanceof ClosureExpression)) internalError("Expected closure value for annotation parameter 'value'. Found $member")
+    conditionNode = member;
+    conditionMethod = 'conditionalTransform' + node.hashCode() + '$condition'
+    conditionCallExpression = new MethodCallExpression(new VariableExpression('this'), conditionMethod, new ArgumentListExpression())
+  }
+
+  protected String getErrorMessage() {
+    'Execution interrupted. The following condition failed: ' + convertClosureToSource(conditionNode)
+  }
+
+  void visitClass(ClassNode type) {
+    currentClass = type
+    def method = type.addMethod(conditionMethod, ACC_PRIVATE | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, conditionNode.code)
+    method.synthetic = true
+    if (applyToAllMembers) {
+      super.visitClass(type)
+    }
+  }
+
+  protected Expression createCondition() {
+    conditionCallExpression
+  }
+
+  @Override
+  void visitAnnotations(AnnotatedNode node) {
+    // this transformation does not apply on annotation nodes
+    // visiting could lead to stack overflows
+  }
+
+  @Override
+  void visitField(FieldNode node) {
+    if (!node.isStatic() && !node.isSynthetic()) {
+      super.visitField node
+    }
+  }
+
+  @Override
+  void visitProperty(PropertyNode node) {
+    if (!node.isStatic() && !node.isSynthetic()) {
+      super.visitProperty node
+    }
+  }
+
+  @Override
+  void visitClosureExpression(ClosureExpression closureExpr) {
+    if (closureExpr == conditionNode) return // do not visit the closure from the annotation itself
+    def code = closureExpr.code
+    closureExpr.code = wrapBlock(code)
+    super.visitClosureExpression closureExpr
+  }
+
+  @Override
+  void visitMethod(MethodNode node) {
+    if (node.name == conditionMethod && !node.isSynthetic()) return // do not visit the generated method
+    if (node.name == 'run' && currentClass.isScript() && node.parameters.length == 0) {
+      // the run() method should not have the statement added, otherwise the script binding won't be set before
+      // the condition is actually tested
+      super.visitMethod(node)
+    } else {
+      if (checkOnMethodStart && !node.isSynthetic() && !node.isStatic() && !node.isAbstract()) {
+        def code = node.code
+        node.code = wrapBlock(code);
+      }
+      if (!node.isSynthetic() && !node.isStatic()) super.visitMethod(node)
+    }
+  }
+
+  /**
+   * Converts a ClosureExpression into the String source.
+   * @param expression a closure
+   * @return the source the closure was created from
+   */
+  private String convertClosureToSource(ClosureExpression expression) {
+    try {
+        return ClosureUtils.convertClosureToSource(this.source.source, expression);
+    } catch(Exception e) {
+        return e.message
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/a6c82ad0/src/main/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy b/src/main/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy
new file mode 100644
index 0000000..a4fb4c3
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy
@@ -0,0 +1,98 @@
+/*
+ *  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.transform
+
+import groovy.transform.CompileStatic
+import groovy.transform.ThreadInterrupt
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.Parameter
+import org.codehaus.groovy.ast.expr.ArgumentListExpression
+import org.codehaus.groovy.ast.expr.ClassExpression
+import org.codehaus.groovy.ast.expr.ClosureExpression
+import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.control.CompilePhase
+
+/**
+ * Allows "interrupt-safe" executions of scripts by adding Thread.currentThread().isInterrupted()
+ * checks on loops (for, while, do) and first statement of closures. By default, also adds an interrupt check
+ * statement on the beginning of method calls.
+ *
+ * @see groovy.transform.ThreadInterrupt
+ *
+ * @author Cedric Champeau
+ * @author Hamlet D'Arcy
+ *
+ * @since 1.8.0
+ */
+@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
+@CompileStatic
+public class ThreadInterruptibleASTTransformation extends AbstractInterruptibleASTTransformation {
+
+    private static final ClassNode MY_TYPE = ClassHelper.make(ThreadInterrupt)
+    private static final ClassNode THREAD_TYPE = ClassHelper.make(Thread)
+    private static final MethodNode CURRENTTHREAD_METHOD
+    private static final MethodNode ISINTERRUPTED_METHOD
+
+    static {
+        CURRENTTHREAD_METHOD = THREAD_TYPE.getMethod('currentThread', Parameter.EMPTY_ARRAY)
+        ISINTERRUPTED_METHOD = THREAD_TYPE.getMethod('isInterrupted', Parameter.EMPTY_ARRAY)
+    }
+
+    protected ClassNode type() {
+        return MY_TYPE;
+    }
+
+    protected String getErrorMessage() {
+        'Execution interrupted. The current thread has been interrupted.'
+    }
+
+    protected Expression createCondition() {
+        def currentThread = new MethodCallExpression(new ClassExpression(THREAD_TYPE),
+                'currentThread',
+                ArgumentListExpression.EMPTY_ARGUMENTS)
+        currentThread.methodTarget = CURRENTTHREAD_METHOD
+        def isInterrupted = new MethodCallExpression(
+                currentThread,
+                'isInterrupted', ArgumentListExpression.EMPTY_ARGUMENTS)
+        isInterrupted.methodTarget = ISINTERRUPTED_METHOD
+        [currentThread, isInterrupted]*.implicitThis = false
+
+        isInterrupted
+    }
+
+
+    @Override
+    public void visitClosureExpression(ClosureExpression closureExpr) {
+        def code = closureExpr.code
+        closureExpr.code = wrapBlock(code)
+        super.visitClosureExpression closureExpr
+    }
+
+    @Override
+    public void visitMethod(MethodNode node) {
+        if (checkOnMethodStart && !node.isSynthetic() && !node.isAbstract()) {
+            def code = node.code
+            node.code = wrapBlock(code);
+        }
+        super.visitMethod(node)
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/a6c82ad0/src/main/groovy/org/codehaus/groovy/transform/TimedInterruptibleASTTransformation.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/org/codehaus/groovy/transform/TimedInterruptibleASTTransformation.groovy b/src/main/groovy/org/codehaus/groovy/transform/TimedInterruptibleASTTransformation.groovy
new file mode 100644
index 0000000..fbc923b
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/TimedInterruptibleASTTransformation.groovy
@@ -0,0 +1,321 @@
+/*
+ *  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.transform
+
+import groovy.transform.TimedInterrupt
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.ast.AnnotatedNode
+import org.codehaus.groovy.ast.AnnotationNode
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.FieldNode
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.PropertyNode
+import org.codehaus.groovy.ast.expr.ClosureExpression
+import org.codehaus.groovy.ast.expr.ConstantExpression
+import org.codehaus.groovy.ast.expr.DeclarationExpression
+import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.stmt.BlockStatement
+import org.codehaus.groovy.ast.stmt.DoWhileStatement
+import org.codehaus.groovy.ast.stmt.ForStatement
+import org.codehaus.groovy.ast.stmt.WhileStatement
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.control.SourceUnit
+
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+
+import static org.codehaus.groovy.ast.ClassHelper.make
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ltX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.plusX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX
+
+/**
+ * Allows "interrupt-safe" executions of scripts by adding timer expiration
+ * checks on loops (for, while, do) and first statement of closures. By default,
+ * also adds an interrupt check statement on the beginning of method calls.
+ *
+ * @author Cedric Champeau
+ * @author Hamlet D'Arcy
+ * @author Paul King
+ * @see groovy.transform.ThreadInterrupt
+ * @since 1.8.0
+ */
+@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
+public class TimedInterruptibleASTTransformation extends AbstractASTTransformation {
+
+    private static final ClassNode MY_TYPE = make(TimedInterrupt)
+    private static final String CHECK_METHOD_START_MEMBER = 'checkOnMethodStart'
+    private static final String APPLY_TO_ALL_CLASSES = 'applyToAllClasses'
+    private static final String APPLY_TO_ALL_MEMBERS = 'applyToAllMembers'
+    private static final String THROWN_EXCEPTION_TYPE = "thrown"
+
+    public void visit(ASTNode[] nodes, SourceUnit source) {
+        init(nodes, source);
+        AnnotationNode node = nodes[0]
+        AnnotatedNode annotatedNode = nodes[1]
+        if (!MY_TYPE.equals(node.getClassNode())) {
+            internalError("Transformation called from wrong annotation: $node.classNode.name")
+        }
+
+        def checkOnMethodStart = getConstantAnnotationParameter(node, CHECK_METHOD_START_MEMBER, Boolean.TYPE, true)
+        def applyToAllMembers = getConstantAnnotationParameter(node, APPLY_TO_ALL_MEMBERS, Boolean.TYPE, true)
+        def applyToAllClasses = applyToAllMembers ? getConstantAnnotationParameter(node, APPLY_TO_ALL_CLASSES, Boolean.TYPE, true) : false
+        def maximum = getConstantAnnotationParameter(node, 'value', Long.TYPE, Long.MAX_VALUE)
+        def thrown = AbstractInterruptibleASTTransformation.getClassAnnotationParameter(node, THROWN_EXCEPTION_TYPE, make(TimeoutException))
+
+        Expression unit = node.getMember('unit') ?: propX(classX(TimeUnit), "SECONDS")
+
+        // should be limited to the current SourceUnit or propagated to the whole CompilationUnit
+        // DO NOT inline visitor creation in code below. It has state that must not persist between calls
+        if (applyToAllClasses) {
+            // guard every class and method defined in this script
+            source.getAST()?.classes?.each { ClassNode it ->
+                def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
+                visitor.visitClass(it)
+            }
+        } else if (annotatedNode instanceof ClassNode) {
+            // only guard this particular class
+            def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
+            visitor.visitClass annotatedNode
+        } else if (!applyToAllMembers && annotatedNode instanceof MethodNode) {
+            // only guard this particular method (plus initCode for class)
+            def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
+            visitor.visitMethod annotatedNode
+            visitor.visitClass annotatedNode.declaringClass
+        } else if (!applyToAllMembers && annotatedNode instanceof FieldNode) {
+            // only guard this particular field (plus initCode for class)
+            def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
+            visitor.visitField annotatedNode
+            visitor.visitClass annotatedNode.declaringClass
+        } else if (!applyToAllMembers && annotatedNode instanceof DeclarationExpression) {
+            // only guard this particular declaration (plus initCode for class)
+            def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
+            visitor.visitDeclarationExpression annotatedNode
+            visitor.visitClass annotatedNode.declaringClass
+        } else {
+            // only guard the script class
+            source.getAST()?.classes?.each { ClassNode it ->
+                if (it.isScript()) {
+                    def visitor = new TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, node.hashCode())
+                    visitor.visitClass(it)
+                }
+            }
+        }
+    }
+
+    static def getConstantAnnotationParameter(AnnotationNode node, String parameterName, Class type, defaultValue) {
+        def member = node.getMember(parameterName)
+        if (member) {
+            if (member instanceof ConstantExpression) {
+                // TODO not sure this try offers value - testing Groovy annotation type handing - throw GroovyBugError or remove?
+                try {
+                    return member.value.asType(type)
+                } catch (ignore) {
+                    internalError("Expecting boolean value for ${parameterName} annotation parameter. Found $member")
+                }
+            } else {
+                internalError("Expecting boolean value for ${parameterName} annotation parameter. Found $member")
+            }
+        }
+        return defaultValue
+    }
+
+    private static void internalError(String message) {
+        throw new RuntimeException("Internal error: $message")
+    }
+
+    private static class TimedInterruptionVisitor extends ClassCodeVisitorSupport {
+        final private SourceUnit source
+        final private boolean checkOnMethodStart
+        final private boolean applyToAllClasses
+        final private boolean applyToAllMembers
+        private FieldNode expireTimeField = null
+        private FieldNode startTimeField = null
+        private final Expression unit
+        private final maximum
+        private final ClassNode thrown
+        private final String basename
+
+        TimedInterruptionVisitor(source, checkOnMethodStart, applyToAllClasses, applyToAllMembers, maximum, unit, thrown, hash) {
+            this.source = source
+            this.checkOnMethodStart = checkOnMethodStart
+            this.applyToAllClasses = applyToAllClasses
+            this.applyToAllMembers = applyToAllMembers
+            this.unit = unit
+            this.maximum = maximum
+            this.thrown = thrown
+            this.basename = 'timedInterrupt' + hash
+        }
+
+        /**
+         * @return Returns the interruption check statement.
+         */
+        final createInterruptStatement() {
+            ifS(
+
+                    ltX(
+                            propX(varX("this"), basename + '$expireTime'),
+                            callX(make(System), 'nanoTime')
+                    ),
+                    throwS(
+                            ctorX(thrown,
+                                    args(
+                                            plusX(
+                                                    plusX(
+                                                            constX('Execution timed out after ' + maximum + ' '),
+                                                            callX(callX(unit, 'name'), 'toLowerCase', propX(classX(Locale), 'US'))
+                                                    ),
+                                                    plusX(
+                                                            constX('. Start time: '),
+                                                            propX(varX("this"), basename + '$startTime')
+                                                    )
+                                            )
+
+                                    )
+                            )
+                    )
+            )
+        }
+
+        /**
+         * Takes a statement and wraps it into a block statement which first element is the interruption check statement.
+         * @param statement the statement to be wrapped
+         * @return a {@link BlockStatement block statement}    which first element is for checking interruption, and the
+         * second one the statement to be wrapped.
+         */
+        private wrapBlock(statement) {
+            def stmt = new BlockStatement();
+            stmt.addStatement(createInterruptStatement());
+            stmt.addStatement(statement);
+            stmt
+        }
+
+        @Override
+        void visitClass(ClassNode node) {
+            if (node.getDeclaredField(basename + '$expireTime')) {
+                return
+            }
+            expireTimeField = node.addField(basename + '$expireTime',
+                    ACC_FINAL | ACC_PRIVATE,
+                    ClassHelper.long_TYPE,
+                    plusX(
+                            callX(make(System), 'nanoTime'),
+                            callX(
+                                    propX(classX(TimeUnit), 'NANOSECONDS'),
+                                    'convert',
+                                    args(constX(maximum, true), unit)
+                            )
+                    )
+            );
+            expireTimeField.synthetic = true
+            startTimeField = node.addField(basename + '$startTime',
+                    ACC_FINAL | ACC_PRIVATE,
+                    make(Date),
+                    ctorX(make(Date))
+            )
+            startTimeField.synthetic = true
+
+            // force these fields to be initialized first
+            node.fields.remove(expireTimeField)
+            node.fields.remove(startTimeField)
+            node.fields.add(0, startTimeField)
+            node.fields.add(0, expireTimeField)
+            if (applyToAllMembers) {
+                super.visitClass node
+            }
+        }
+
+        @Override
+        void visitClosureExpression(ClosureExpression closureExpr) {
+            def code = closureExpr.code
+            if (code instanceof BlockStatement) {
+                code.statements.add(0, createInterruptStatement())
+            } else {
+                closureExpr.code = wrapBlock(code)
+            }
+            super.visitClosureExpression closureExpr
+        }
+
+        @Override
+        void visitField(FieldNode node) {
+            if (!node.isStatic() && !node.isSynthetic()) {
+                super.visitField node
+            }
+        }
+
+        @Override
+        void visitProperty(PropertyNode node) {
+            if (!node.isStatic() && !node.isSynthetic()) {
+                super.visitProperty node
+            }
+        }
+
+        /**
+         * Shortcut method which avoids duplicating code for every type of loop.
+         * Actually wraps the loopBlock of different types of loop statements.
+         */
+        private visitLoop(loopStatement) {
+            def statement = loopStatement.loopBlock
+            loopStatement.loopBlock = wrapBlock(statement)
+        }
+
+        @Override
+        void visitForLoop(ForStatement forStatement) {
+            visitLoop(forStatement)
+            super.visitForLoop(forStatement)
+        }
+
+        @Override
+        void visitDoWhileLoop(final DoWhileStatement doWhileStatement) {
+            visitLoop(doWhileStatement)
+            super.visitDoWhileLoop(doWhileStatement)
+        }
+
+        @Override
+        void visitWhileLoop(final WhileStatement whileStatement) {
+            visitLoop(whileStatement)
+            super.visitWhileLoop(whileStatement)
+        }
+
+        @Override
+        void visitMethod(MethodNode node) {
+            if (checkOnMethodStart && !node.isSynthetic() && !node.isStatic() && !node.isAbstract()) {
+                def code = node.code
+                node.code = wrapBlock(code);
+            }
+            if (!node.isSynthetic() && !node.isStatic()) {
+                super.visitMethod(node)
+            }
+        }
+
+        protected SourceUnit getSourceUnit() {
+            return source;
+        }
+    }
+}


Mime
View raw message