Class CpsTransformer

java.lang.Object
org.codehaus.groovy.control.CompilationUnit.PrimaryClassNodeOperation
org.codehaus.groovy.control.customizers.CompilationCustomizer
com.cloudbees.groovy.cps.CpsTransformer
All Implemented Interfaces:
org.codehaus.groovy.ast.GroovyCodeVisitor
Direct Known Subclasses:
SandboxCpsTransformer

public class CpsTransformer extends org.codehaus.groovy.control.customizers.CompilationCustomizer implements org.codehaus.groovy.ast.GroovyCodeVisitor
Performs CPS transformation of Groovy methods.

Every method not annotated with NonCPS gets rewritten. The general strategy of CPS transformation is as follows:

Before:

 Object foo(int x, int y) {
   return x+y;
 }
 

After:

 Object foo(int x, int y) {
   // the first part is AST of the method body
   // the rest (including implicit receiver argument) is actual value of arguments
   throw new CpsCallableInvocation(___cps___N, this, new Object[] {x, y});
 }

 private static CpsFunction ___cps___N = ___cps___N();

 private static final CpsFunction ___cps___N() {
   Builder b = new Builder(...);
   return new CpsFunction(['x','y'], b.plus(b.localVariable("x"), b.localVariable("y"))
 }
 

That is, we transform a Groovy AST of the method body into a tree of Blocks by using Builder, then the method just returns this function object and expect the caller to evaluate it, instead of executing the method synchronously before it returns.

This class achieves this transformation by implementing GroovyCodeVisitor and traverse Groovy AST tree in the in-order. As we traverse this tree, we produce another Groovy AST tree that invokes Builder. Note that we aren't calling Builder directly here; that's supposed to happen when the Groovy code under transformation actually runs.

Groovy AST that calls Builder is a tree of function call, so we build MethodCallExpressions in the top-down manner. We do this by makeNode(String, Runnable), which creates a call to Builder.xxx(...), then supply the closure that fills in the arguments to this call by walking down the original Groovy AST tree. This walk-down is done by calling visit(ASTNode) (to recursively visit ASTs), or by calling literal(String) methods, which generate string/class/etc literals, as sometimes Builder methods need them as well.

Author:
Kohsuke Kawaguchi
  • Field Details

  • Constructor Details

    • CpsTransformer

      public CpsTransformer()
  • Method Details

    • setConfiguration

      public void setConfiguration(@NonNull TransformerConfiguration config)
    • call

      public void call(org.codehaus.groovy.control.SourceUnit source, org.codehaus.groovy.classgen.GeneratorContext context, org.codehaus.groovy.ast.ClassNode classNode)
      Specified by:
      call in class org.codehaus.groovy.control.CompilationUnit.PrimaryClassNodeOperation
    • processConstructors

      protected void processConstructors(org.codehaus.groovy.ast.ClassNode classNode)
      Constructors can't be transformed - if we throw a CpsCallableInvocation from inside a constructor, there's no way to get back to the continuation. The object does not get created and so we're unable to proceed with it. The same thing applies for object initializers.
    • shouldBeTransformed

      protected boolean shouldBeTransformed(org.codehaus.groovy.ast.MethodNode node)
      Should this method be transformed?
    • visitMethod

      public void visitMethod(org.codehaus.groovy.ast.MethodNode m)
      Transforms asynchronous workflow method. From:
      
       ReturnT foo( T1 arg1, T2 arg2, ...) { ... body ... }
       
      To:
      
       private static CpsFunction ___cps___N = ___cps___N();
      
       private static final CpsFunction ___cps___N() {
           return new CpsFunction(['arg1','arg2','arg3',...], CPS-transformed-method-body)
       }
      
       ReturnT foo( T1 arg1, T2 arg2, ...) {
           throw new CpsCallableInvocation(___cps___N, this, new Object[] {arg1, arg2, ...}) }
       
    • makeBuilder

      protected org.codehaus.groovy.ast.expr.Expression makeBuilder(org.codehaus.groovy.ast.MethodNode m)
      Generates code that instantiates a new Builder.

      Hook for subtypes to tweak builder, for example to Builder.contextualize(com.cloudbees.groovy.cps.sandbox.CallSiteTag...)

       Builder b = new Builder(new MethodLocation(...));
       b.withClosureType(...);
       
      Parameters:
      m - Method being transformed.
    • getTrustTag

      protected Class getTrustTag()
      Trusted or Untrusted tag that gets added to call site.
      See Also:
      • "doc/sandbox.md"
    • visitNontransformedMethod

      protected void visitNontransformedMethod(org.codehaus.groovy.ast.MethodNode m)
      For methods that are not CPS-transformed.
    • visitNontransformedField

      protected void visitNontransformedField(org.codehaus.groovy.ast.FieldNode f)
    • visitNontransformedStatement

      protected void visitNontransformedStatement(org.codehaus.groovy.ast.stmt.Statement s)
    • visit

      protected void visit(org.codehaus.groovy.ast.ASTNode e)
    • visit

      protected void visit(Collection<? extends org.codehaus.groovy.ast.ASTNode> col)
    • visitWithSafepoint

      protected void visitWithSafepoint(org.codehaus.groovy.ast.stmt.Statement st)
      Like visit(ASTNode) but also inserts the safepoint at the top.
    • makeNode

      protected void makeNode(String methodName, org.codehaus.groovy.ast.expr.Expression... args)
      Makes an AST fragment that calls Builder with specific method.
      Parameters:
      methodName - Method on Builder to call.
    • makeNode

      protected void makeNode(String methodName, Runnable body)
      Makes an AST fragment that calls Builder with specific method.
      Parameters:
      methodName - Method on Builder to call.
    • makeNode

      protected void makeNode(org.codehaus.groovy.ast.ClassNode type, org.codehaus.groovy.ast.expr.Expression... args)
      Makes an AST fragment that instantiates a new instance of the given type.
    • makeNode

      protected void makeNode(org.codehaus.groovy.ast.ClassNode type, Runnable body)
      Makes an AST fragment that instantiates a new instance of the given type.
    • makeChildren

      protected org.codehaus.groovy.ast.expr.TupleExpression makeChildren(org.codehaus.groovy.ast.expr.Expression... args)
      Shorthand for TupleExpression(Expression[]).
    • makeChildren

      protected org.codehaus.groovy.ast.expr.TupleExpression makeChildren(Runnable body)
      Given closure, package them up into a tuple.
    • loc

      protected void loc(org.codehaus.groovy.ast.ASTNode e)
    • literal

      protected void literal(String s)
      Used in the closure block of makeNode(String, Runnable) to create a literal string argument.
    • literal

      protected void literal(org.codehaus.groovy.ast.ClassNode c)
    • literal

      protected void literal(int n)
    • literal

      protected void literal(boolean b)
    • visitMethodCallExpression

      public void visitMethodCallExpression(org.codehaus.groovy.ast.expr.MethodCallExpression call)
      Specified by:
      visitMethodCallExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitBlockStatement

      public void visitBlockStatement(org.codehaus.groovy.ast.stmt.BlockStatement b)
      Specified by:
      visitBlockStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitForLoop

      public void visitForLoop(org.codehaus.groovy.ast.stmt.ForStatement forLoop)
      Specified by:
      visitForLoop in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitWhileLoop

      public void visitWhileLoop(org.codehaus.groovy.ast.stmt.WhileStatement loop)
      Specified by:
      visitWhileLoop in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitDoWhileLoop

      public void visitDoWhileLoop(org.codehaus.groovy.ast.stmt.DoWhileStatement loop)
      Specified by:
      visitDoWhileLoop in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitIfElse

      public void visitIfElse(org.codehaus.groovy.ast.stmt.IfStatement stmt)
      Specified by:
      visitIfElse in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitExpressionStatement

      public void visitExpressionStatement(org.codehaus.groovy.ast.stmt.ExpressionStatement statement)
      Specified by:
      visitExpressionStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitReturnStatement

      public void visitReturnStatement(org.codehaus.groovy.ast.stmt.ReturnStatement statement)
      Specified by:
      visitReturnStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitAssertStatement

      public void visitAssertStatement(org.codehaus.groovy.ast.stmt.AssertStatement statement)
      Specified by:
      visitAssertStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitTryCatchFinally

      public void visitTryCatchFinally(org.codehaus.groovy.ast.stmt.TryCatchStatement stmt)
      Specified by:
      visitTryCatchFinally in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitSwitch

      public void visitSwitch(org.codehaus.groovy.ast.stmt.SwitchStatement stmt)
      Specified by:
      visitSwitch in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitCaseStatement

      public void visitCaseStatement(org.codehaus.groovy.ast.stmt.CaseStatement stmt)
      Specified by:
      visitCaseStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitBreakStatement

      public void visitBreakStatement(org.codehaus.groovy.ast.stmt.BreakStatement statement)
      Specified by:
      visitBreakStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitContinueStatement

      public void visitContinueStatement(org.codehaus.groovy.ast.stmt.ContinueStatement statement)
      Specified by:
      visitContinueStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitThrowStatement

      public void visitThrowStatement(org.codehaus.groovy.ast.stmt.ThrowStatement st)
      Specified by:
      visitThrowStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitSynchronizedStatement

      public void visitSynchronizedStatement(org.codehaus.groovy.ast.stmt.SynchronizedStatement statement)
      Specified by:
      visitSynchronizedStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitCatchStatement

      public void visitCatchStatement(org.codehaus.groovy.ast.stmt.CatchStatement stmt)
      Specified by:
      visitCatchStatement in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitStaticMethodCallExpression

      public void visitStaticMethodCallExpression(org.codehaus.groovy.ast.expr.StaticMethodCallExpression exp)
      Specified by:
      visitStaticMethodCallExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitConstructorCallExpression

      public void visitConstructorCallExpression(org.codehaus.groovy.ast.expr.ConstructorCallExpression call)
      Specified by:
      visitConstructorCallExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitTernaryExpression

      public void visitTernaryExpression(org.codehaus.groovy.ast.expr.TernaryExpression exp)
      Specified by:
      visitTernaryExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitShortTernaryExpression

      public void visitShortTernaryExpression(org.codehaus.groovy.ast.expr.ElvisOperatorExpression exp)
      Specified by:
      visitShortTernaryExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • getMultipleAssignmentValueOrCast

      protected void getMultipleAssignmentValueOrCast(org.codehaus.groovy.ast.expr.VariableExpression varExp, org.codehaus.groovy.ast.expr.Expression rhs, org.codehaus.groovy.ast.expr.Expression index)
    • visitBinaryExpression

      public void visitBinaryExpression(org.codehaus.groovy.ast.expr.BinaryExpression exp)
      Specified by:
      visitBinaryExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
      See Also:
      • BinaryExpressionHelper.eval(BinaryExpression)
    • visitPrefixExpression

      public void visitPrefixExpression(org.codehaus.groovy.ast.expr.PrefixExpression exp)
      Specified by:
      visitPrefixExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitPostfixExpression

      public void visitPostfixExpression(org.codehaus.groovy.ast.expr.PostfixExpression exp)
      Specified by:
      visitPostfixExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • prepostfixOperatorSuffix

      protected String prepostfixOperatorSuffix(org.codehaus.groovy.syntax.Token operation)
    • visitBooleanExpression

      public void visitBooleanExpression(org.codehaus.groovy.ast.expr.BooleanExpression exp)
      Specified by:
      visitBooleanExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitClosureExpression

      public void visitClosureExpression(org.codehaus.groovy.ast.expr.ClosureExpression exp)
      Specified by:
      visitClosureExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitTupleExpression

      public void visitTupleExpression(org.codehaus.groovy.ast.expr.TupleExpression expression)
      Specified by:
      visitTupleExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitMapExpression

      public void visitMapExpression(org.codehaus.groovy.ast.expr.MapExpression exp)
      Specified by:
      visitMapExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitMapEntryExpression

      public void visitMapEntryExpression(org.codehaus.groovy.ast.expr.MapEntryExpression expression)
      Specified by:
      visitMapEntryExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitListExpression

      public void visitListExpression(org.codehaus.groovy.ast.expr.ListExpression exp)
      Specified by:
      visitListExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitRangeExpression

      public void visitRangeExpression(org.codehaus.groovy.ast.expr.RangeExpression exp)
      Specified by:
      visitRangeExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitPropertyExpression

      public void visitPropertyExpression(org.codehaus.groovy.ast.expr.PropertyExpression exp)
      Specified by:
      visitPropertyExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitAttributeExpression

      public void visitAttributeExpression(org.codehaus.groovy.ast.expr.AttributeExpression exp)
      Specified by:
      visitAttributeExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitFieldExpression

      public void visitFieldExpression(org.codehaus.groovy.ast.expr.FieldExpression exp)
      Specified by:
      visitFieldExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitMethodPointerExpression

      public void visitMethodPointerExpression(org.codehaus.groovy.ast.expr.MethodPointerExpression exp)
      Specified by:
      visitMethodPointerExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitConstantExpression

      public void visitConstantExpression(org.codehaus.groovy.ast.expr.ConstantExpression expression)
      Specified by:
      visitConstantExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitClassExpression

      public void visitClassExpression(org.codehaus.groovy.ast.expr.ClassExpression expression)
      Specified by:
      visitClassExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitVariableExpression

      public void visitVariableExpression(org.codehaus.groovy.ast.expr.VariableExpression exp)
      Specified by:
      visitVariableExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitDeclarationExpression

      public void visitDeclarationExpression(org.codehaus.groovy.ast.expr.DeclarationExpression exp)
      Specified by:
      visitDeclarationExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitAssignmentOrCast

      protected void visitAssignmentOrCast(org.codehaus.groovy.ast.ClassNode type, org.codehaus.groovy.ast.expr.Expression rhs)
    • visitGStringExpression

      public void visitGStringExpression(org.codehaus.groovy.ast.expr.GStringExpression exp)
      Specified by:
      visitGStringExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitArrayExpression

      public void visitArrayExpression(org.codehaus.groovy.ast.expr.ArrayExpression exp)
      Specified by:
      visitArrayExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitSpreadExpression

      public void visitSpreadExpression(org.codehaus.groovy.ast.expr.SpreadExpression expression)
      Specified by:
      visitSpreadExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitSpreadMapExpression

      public void visitSpreadMapExpression(org.codehaus.groovy.ast.expr.SpreadMapExpression expression)
      Specified by:
      visitSpreadMapExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitNotExpression

      public void visitNotExpression(org.codehaus.groovy.ast.expr.NotExpression exp)
      Specified by:
      visitNotExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitUnaryMinusExpression

      public void visitUnaryMinusExpression(org.codehaus.groovy.ast.expr.UnaryMinusExpression exp)
      Specified by:
      visitUnaryMinusExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitUnaryPlusExpression

      public void visitUnaryPlusExpression(org.codehaus.groovy.ast.expr.UnaryPlusExpression exp)
      Specified by:
      visitUnaryPlusExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitBitwiseNegationExpression

      public void visitBitwiseNegationExpression(org.codehaus.groovy.ast.expr.BitwiseNegationExpression exp)
      Specified by:
      visitBitwiseNegationExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitCastExpression

      public void visitCastExpression(org.codehaus.groovy.ast.expr.CastExpression exp)
      Specified by:
      visitCastExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitArgumentlistExpression

      public void visitArgumentlistExpression(org.codehaus.groovy.ast.expr.ArgumentListExpression expression)
      Specified by:
      visitArgumentlistExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitClosureListExpression

      public void visitClosureListExpression(org.codehaus.groovy.ast.expr.ClosureListExpression closureListExpression)
      Specified by:
      visitClosureListExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor
    • visitBytecodeExpression

      public void visitBytecodeExpression(org.codehaus.groovy.classgen.BytecodeExpression expression)
      Specified by:
      visitBytecodeExpression in interface org.codehaus.groovy.ast.GroovyCodeVisitor