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

Collection.inject fails in CPS-transformed methods with closure stored in class field

XMLWordPrintable

      When calling Collection.inject from a CPS-transformed method using a class field as the closure parameter, the closure gets called with a single list instead of the usual two parameters. This list contains the parameters that should have been used.

      This doesn't happen when the calling method is marked @NonCPS or when the closure is a local variable (even a tiny wrapper calling the original class field). So there is an easy workaround, but I still think this is a bug.

      Versions:

      • Jenkins 2.138.1
      • workflow-cps 2.57
      • workflow-cps-global-lib 2.12

      Example pipeline script:

      class Test implements Serializable {
          def script
          Closure right = {l, e -> script.println "got l='$l', e='$e'"; l + e*10}
          Closure wrong = {script.println "got '$it'"; it[0] + it[1]*10}
      
          Test(script) {
              this.script = script
          }
          
          def testRight() {
              def x = [1,2,3].inject([], right)
              script.println x
          }
      
          @NonCPS
          def nonCPSRight() {
              def x = [1,2,3].inject([], right)
              script.println x
          }
      
          def localClosureRight() {
              Closure right = {l, e -> script.println "got l='$l', e='$e'"; l + e*10}
              def x = [1,2,3].inject([], right)
              script.println x
          }
      
          def localWrapperRight() {
              Closure cl = {l,e -> right.call(l,e)}
              def x = [1,2,3].inject([], cl)
              script.println x
          }
          
          def testWrong() {
              def x = [1,2,3].inject([], wrong)
              script.println x
          }
      }
      
      def t = new Test(this)
      println "--- wrong ---"
      t.testWrong() // this should not work, but does
      println "--- NonCPS right ---"
      t.nonCPSRight() // this works
      println "--- local closure right ---"
      t.localClosureRight() // this works
      println "--- local wrapper right ---"
      t.localWrapperRight() // this works
      println "--- right ---"
      t.testRight() // this fails
      

      Build log:

      [Pipeline] echo
      --- wrong ---
      [Pipeline] echo
      got '[[], 1]'
      [Pipeline] echo
      got '[[10], 2]'
      [Pipeline] echo
      got '[[10, 20], 3]'
      [Pipeline] echo
      [10, 20, 30]
      [Pipeline] echo
      --- NonCPS right ---
      [Pipeline] echo
      got l='[]', e='1'
      [Pipeline] echo
      got l='[10]', e='2'
      [Pipeline] echo
      got l='[10, 20]', e='3'
      [Pipeline] echo
      [10, 20, 30]
      [Pipeline] echo
      --- local closure right ---
      [Pipeline] echo
      got l='[]', e='1'
      [Pipeline] echo
      got l='[10]', e='2'
      [Pipeline] echo
      got l='[10, 20]', e='3'
      [Pipeline] echo
      [10, 20, 30]
      [Pipeline] echo
      --- local wrapper right ---
      [Pipeline] echo
      got l='[]', e='1'
      [Pipeline] echo
      got l='[10]', e='2'
      [Pipeline] echo
      got l='[10, 20]', e='3'
      [Pipeline] echo
      [10, 20, 30]
      [Pipeline] echo
      --- right ---
      [Pipeline] End of Pipeline
      hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: Test$_closure1.call() is applicable for argument types: ([Ljava.lang.Object;) values: [[[], 1]]
      Possible solutions: any(), any(), any(groovy.lang.Closure), use([Ljava.lang.Object;), each(groovy.lang.Closure), any(groovy.lang.Closure)
      	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:286)
      	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
      	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:810)
      	at groovy.lang.GroovyObjectSupport.invokeMethod(GroovyObjectSupport.java:46)
      	at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:48)
      	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
      	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
      	at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.methodCall(DefaultInvoker.java:20)
      	at com.cloudbees.groovy.cps.CpsDefaultGroovyMethods.inject(CpsDefaultGroovyMethods:5383)
      	at com.cloudbees.groovy.cps.CpsDefaultGroovyMethods.inject(CpsDefaultGroovyMethods:5324)
      	at Test.testRight(WorkflowScript:11)
      	at WorkflowScript.run(WorkflowScript:49)
      	at ___cps.transform___(Native Method)
      	at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:57)
      	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:109)
      	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:82)
      	at sun.reflect.GeneratedMethodAccessor175.invoke(Unknown Source)
      	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      	at java.lang.reflect.Method.invoke(Method.java:498)
      	at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
      	at com.cloudbees.groovy.cps.impl.LocalVariableBlock$LocalVariable.get(LocalVariableBlock.java:39)
      	at com.cloudbees.groovy.cps.LValueBlock$GetAdapter.receive(LValueBlock.java:30)
      	at com.cloudbees.groovy.cps.impl.LocalVariableBlock.evalLValue(LocalVariableBlock.java:28)
      	at com.cloudbees.groovy.cps.LValueBlock$BlockImpl.eval(LValueBlock.java:55)
      	at com.cloudbees.groovy.cps.LValueBlock.eval(LValueBlock.java:16)
      	at com.cloudbees.groovy.cps.Next.step(Next.java:83)
      	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:174)
      	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:163)
      	at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:122)
      	at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:261)
      	at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:163)
      	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$101(SandboxContinuable.java:34)
      	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.lambda$run0$0(SandboxContinuable.java:59)
      	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:108)
      	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:58)
      	at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:182)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:332)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$200(CpsThreadGroup.java:83)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:244)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:232)
      	at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:64)
      	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      	at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:131)
      	at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
      	at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:59)
      	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
      	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
      	at java.lang.Thread.run(Thread.java:748)
      

            Unassigned Unassigned
            leromarinvit leromarinvit
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: