Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-26313

Workflow script fails if CPS-transformed methods are called from constructors

    • Pipeline - April 2018

      When a CPS-transformed method is called from a Groovy class constructor, the CpsCallableInvocation is not caught and aborts the build.

      Here is a minimally-reproducible test case:

      class B { 
          static def sqr(x) { x*x } 
      }
      
      class A { 
           A(y) { 
              B.sqr(y)
           }
      }
      
      def a = new A(42)
      

      The resulting CpsCallableInvocation is not caught and the build aborts:

      Running: End of Workflow
      com.cloudbees.groovy.cps.impl.CpsCallableInvocation
      	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
      	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
      	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
      	at java.lang.reflect.Constructor.newInstance(Constructor.java:534)
      	at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
      	at org.codehaus.groovy.reflection.CachedConstructor.doConstructorInvoke(CachedConstructor.java:71)
      	at org.codehaus.groovy.runtime.callsite.ConstructorSite.callConstructor(ConstructorSite.java:42)
      	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54)
      	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
      	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:198)
      	at B.sqr(WorkflowScript)
      	at B$sqr.call(Unknown Source)
      	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
      	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
      	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
      	at A.<init>(WorkflowScript:11)
      	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
      	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
      	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
      	at java.lang.reflect.Constructor.newInstance(Constructor.java:534)
      	at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
      	at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
      	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54)
      	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
      	at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.constructorCall(DefaultInvoker.java:20)
      	at WorkflowScript.run(WorkflowScript:15)
      	at ___cps.transform___(Native Method)
      	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:90)
      	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:76)
      	at sun.reflect.GeneratedMethodAccessor498.invoke(Unknown Source)
      	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      	at java.lang.reflect.Method.invoke(Method.java:622)
      	at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
      	at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
      	at com.cloudbees.groovy.cps.Next.step(Next.java:58)
      	at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:145)
      	at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:164)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:267)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$000(CpsThreadGroup.java:70)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:176)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:174)
      	at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:47)
      	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
      	at java.util.concurrent.FutureTask.run(FutureTask.java:166)
      	at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:111)
      	at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
      	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
      	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
      	at java.util.concurrent.FutureTask.run(FutureTask.java:166)
      	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1146)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
      	at java.lang.Thread.run(Thread.java:701)
      Finished: FAILURE
      

          [JENKINS-26313] Workflow script fails if CPS-transformed methods are called from constructors

          There is a workaround: annotate sqr() with @com.cloudbees.groovy.cps.NonCPS

          Alexander Bertram added a comment - There is a workaround: annotate sqr() with @com.cloudbees.groovy.cps.NonCPS

          Jesse Glick added a comment -

          I wonder if this is even fixable.

          Jesse Glick added a comment - I wonder if this is even fixable.

          Jesse Glick added a comment -

          At least there is clearly an error (cf. JENKINS-31314). Could perhaps just have the existing error be reported with a clearer message explaining the fundamental limitation.

          Jesse Glick added a comment - At least there is clearly an error (cf. JENKINS-31314 ). Could perhaps just have the existing error be reported with a clearer message explaining the fundamental limitation.

          Ken Lamb added a comment -

          It took me quite a bit of online searching to understand that the problem was I simply need to add "@NonCPS" to the method that I was calling from the constructor. It was especially confusing since the code worked as is in the Jenkins Script Console. An error mesage like "constructors cannot called CPS methods" would have been a big help.

          Ken Lamb added a comment - It took me quite a bit of online searching to understand that the problem was I simply need to add "@NonCPS" to the method that I was calling from the constructor. It was especially confusing since the code worked as is in the Jenkins Script Console. An error mesage like "constructors cannot called CPS methods" would have been a big help.

          Chris Russell added a comment -

          I just ran into the same problem.

          Chris Russell added a comment - I just ran into the same problem.

          Chris Russell added a comment -

          The @NonCPS workaround has to be applied to the entire call stack.

          If it is a fundamental limitation, then a more useful error message would be much appreciated.

          Chris Russell added a comment - The @NonCPS workaround has to be applied to the entire call stack. If it is a fundamental limitation, then a more useful error message would be much appreciated.

          Jesse Glick added a comment -

          klamb crussell52 feel free to vote for JENKINS-31314. (Vote, not comment, unless you have something specific to add that was not already covered by the description or existing comments.) Currently I do not know how to implement it.

          Jesse Glick added a comment - klamb crussell52 feel free to vote for  JENKINS-31314 . (Vote, not comment, unless you have something specific to add that was not already covered by the description or existing comments.) Currently I do not know how to implement it.

          Jesse Glick added a comment -

          Probably this should be closed as Won't Fix.

          Jesse Glick added a comment - Probably this should be closed as Won't Fix.

          Andrew Bayer added a comment -

          Will take a quick look at this soon and if I can't see a solution quickly, I'll close this as won't fix.

          Andrew Bayer added a comment - Will take a quick look at this soon and if I can't see a solution quickly, I'll close this as won't fix.

          Is there any news on this problem? We are encountering the same CpsCallableInvocation message when calling a static method from inside a Groovy class constructor.

          TraceTronic GmbH added a comment - Is there any news on this problem? We are encountering the same CpsCallableInvocation message when calling a static method from inside a Groovy class constructor.

          Andrew Bayer added a comment -

          So I guess the question here is why are constructors not transformed? Same with object initializer statements. I assume there's a good reason, but off the top of my head I'm not sure what that is.

          Andrew Bayer added a comment - So I guess the question here is why are constructors not transformed? Same with object initializer statements. I assume there's a good reason, but off the top of my head I'm not sure what that is.

          Andrew Bayer added a comment -

          Ok, I now know why the constructors aren't transformed. If we throw a CpsCallableInvocation within a constructor (or an object initializer), the result is that we haven't actually created the object, so there's no way back to the continuation. I've opened a PR (https://github.com/cloudbees/groovy-cps/pull/83) adding some javadoc explaining this so that I and others get reminded of this in the future if needed. But that does mean this is a won't fix - without some theoretical and super-gnarly low-level magic, we just can't do this, and that magic is probably a bad idea even before taking into account the added complexity and fragility. Sorry!

          Andrew Bayer added a comment - Ok, I now know why the constructors aren't transformed. If we throw a CpsCallableInvocation within a constructor (or an object initializer), the result is that we haven't actually created the object, so there's no way back to the continuation. I've opened a PR ( https://github.com/cloudbees/groovy-cps/pull/83 ) adding some javadoc explaining this so that I and others get reminded of this in the future if needed. But that does mean this is a won't fix - without some theoretical and super-gnarly low-level magic, we just can't do this, and that magic is probably a bad idea even before taking into account the added complexity and fragility. Sorry!

          Is there any way of putting a nice error message in the jenkins log? It took some time to figure this out. In fact, all of my @NonCps-related problems have been time consuming, especially when you have never heard of it before.

           

          Yngvar Kristiansen added a comment - Is there any way of putting a nice error message in the jenkins log? It took some time to figure this out. In fact, all of my @NonCps-related problems have been time consuming, especially when you have never heard of it before.  

          Jesse Glick added a comment -

          Is there any way of putting a nice error message in the jenkins log?

          JENKINS-31314

          Jesse Glick added a comment - Is there any way of putting a nice error message in the jenkins log? JENKINS-31314

            abayer Andrew Bayer
            akbertram Alexander Bertram
            Votes:
            6 Vote for this issue
            Watchers:
            16 Start watching this issue

              Created:
              Updated:
              Resolved: