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

Incorrect and confusing compilation of sets with custom types

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Major Major
    • workflow-cps-plugin
    • None

      First, create a pipeline job with this pipeline that contains a class definition:

      class Data implements Serializable {
        final String string
        Data(final String string) {
            this.string = Objects.requireNonNull(string)
        }
      
        boolean equals(final other) {
          if (this.is(other)) {
              return true
          }
          if (getClass() != other.class) {
              return false
          }
      
          final Data that = (Data) other
          return string == that.string
        }
      
        int hashCode() {
          return string.hashCode()
        }
      }
      
      final d1 = new Data('a')
      final d2 = new Data('b')
      final d3 = new Data('c')
      
      final s1 = 'a'
      final s2 = 'b'
      final s3 = 'c'
      
      final s0 = [s1, s2, s3]
      echo "s1: ${s0.getClass()}"
      final ss1 = [s1, s2, s3].toSet()
      echo "ss1: ${ss1.getClass()}"
      final Set<String> ss2 = [s1, s2, s3]
      echo "ss2: ${ss2.getClass()}"
      final Set<String> ss3 = [s1, s2, s3].toSet()
      echo "ss3: ${ss3.getClass()}"
      final ss4 = [s1, s2, s3] as Set<String>
      echo "ss4: ${ss4.getClass()}"
      final Set<String> ss5 = [s1, s2, s3] as Set<String>
      echo "ss5: ${ss5.getClass()}"
      
      // Is there some issue with hashCodes?
      // Maps seem to also be affected?
      //final m1 = [:]
      //m1[d1] = s1
      //echo "Map: ${m1.getClass()}"
      //echo "${m1.size()}"
      //echo "Map keys: ${m1.keySet().getClass()}"
      //echo "Map first key: ${m1.keySet().toList()[0].getClass()}"
      
      final d0 = [d1, d2, d3]
      echo "d0: ${s0.getClass()}"
      final ds1 = [d1, d2, d3].toSet()
      echo "ds1: ${ds1.getClass()}"
      
      // Fails with:
      // com.cloudbees.groovy.cps.impl.CpsCallableInvocation
      //final Set<Data> ds2 = [d1, d2, d3]
      //echo "ds2: ${ds2.getClass()}"
      
      // Fails with:
      // hudson.remoting.ProxyException: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '97' with class 'java.lang.Integer' to class 'java.util.Set'
      //final Set<Data> ds3 = [d1, d2, d3].toSet()
      //echo "ds3: ${ds3.getClass()}"
      
      // Fails with:
      // hudson.remoting.ProxyException: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '97' with class 'java.lang.Integer' to class 'java.util.Set'
      //final ds4 = [d1, d2, d3] as Set<Data>
      //echo "ds4: ${ds4.getClass()}"
      
      // Fails with:
      // hudson.remoting.ProxyException: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '97' with class 'java.lang.Integer' to class 'java.util.Set\'
      //final Set<Data> ds5 = [d1, d2, d3] as Set<Data>
      //echo "ds5: ${ds5.getClass()}"
      
      

      Run once, and approve the hashCode() signature since it is not in default whitelist right now:

      Uncomment any of the lines under the cases above, build again, and see failure and confusing types. For example, uncommenting the ds3 example above results in this:

      [Pipeline] echo
      s1: class java.util.ArrayList
      [Pipeline] echo
      ss1: class java.util.HashSet
      [Pipeline] echo
      ss2: class java.util.LinkedHashSet
      [Pipeline] echo
      ss3: class java.util.HashSet
      [Pipeline] echo
      ss4: class java.util.LinkedHashSet
      [Pipeline] echo
      ss5: class java.util.LinkedHashSet
      [Pipeline] echo
      d0: class java.util.ArrayList
      [Pipeline] echo
      ds1: class java.lang.Integer
      [Pipeline] End of Pipeline
      hudson.remoting.ProxyException: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '97' with class 'java.lang.Integer' to class 'java.util.Set'
      	at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnSAM(DefaultTypeTransformation.java:405)
      	at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnNumber(DefaultTypeTransformation.java:319)
      	at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnCollection(DefaultTypeTransformation.java:267)
      	at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToType(DefaultTypeTransformation.java:219)
      	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.java:603)
      	at Unknown.Unknown(Unknown)
      	at ___cps.transform___(Native Method)
      	at com.cloudbees.groovy.cps.impl.LocalVariableBlock$LocalVariable.set(LocalVariableBlock.java:45)
      	at com.cloudbees.groovy.cps.impl.AssignmentBlock$ContinuationImpl.assignAndDone(AssignmentBlock.java:70)
      	at sun.reflect.GeneratedMethodAccessor127.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.ContinuationGroup.methodCall(ContinuationGroup.java:60)
      	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:109)
      	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixName(FunctionCallBlock.java:77)
      	at sun.reflect.GeneratedMethodAccessor109.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.ConstantBlock.eval(ConstantBlock.java:21)
      	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$001(SandboxContinuable.java:19)
      	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable$1.call(SandboxContinuable.java:35)
      	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable$1.call(SandboxContinuable.java:32)
      	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:108)
      	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:32)
      	at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:174)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:331)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$200(CpsThreadGroup.java:82)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:243)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:231)
      	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:112)
      	at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
      	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
            mkobit Mike Kobit
            Votes:
            3 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated: