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

CPS-transformed assignment expressions evaluate to null instead of the value of the RHS

    • workflow-cps 3520.va_8fc49e2f96f

      As described in https://stackoverflow.com/questions/61420959/jenkins-interpretation-of-multiple-object-declarations-on-one-line, CPS-transformed assignment expressions evaluate to null instead of the value of the RHS of the expression. Here is an example:

      def a = b = c = 1 

      In a Pipeline, after executing that expression a == null, b == null, and c == 1.

      In regular Groovy, after executing that expression, a, b, and c, are all equal to 1.

      Here is a (failing) workflow-cps test case (for CpsFlowDefinition2Test) that demonstrates the bug:

      @Test public void assignmentExprsReturnRhs() throws Exception {
          WorkflowJob p = jenkins.createProject(WorkflowJob.class);
          p.setDefinition(new CpsFlowDefinition(
                  "def a = b = c = 1\n" +
                  "println([a, b, c].toString())", true));
          WorkflowRun b = jenkins.buildAndAssertSuccess(p);
          jenkins.assertLogContains("[1, 1, 1]", b); // Actual output today is "[null, null, 1]".
      } 

      I think the relevant code is AssignmentBlock in groovy-cps. In particular, I think that assignAndDone needs to create a new continuation to pass to lhs.set that calls k.receive(rhs) internally.

          [JENKINS-62064] CPS-transformed assignment expressions evaluate to null instead of the value of the RHS

          Devin Nusbaum created issue -
          Devin Nusbaum made changes -
          Summary Original: CPS transformed assignment expressions do not return their result New: CPS-transformed assignment expressions do not return their result
          Devin Nusbaum made changes -
          Summary Original: CPS-transformed assignment expressions do not return their result New: CPS-transformed assignment expressions evaluate to null instead of the value of the RHS
          Devin Nusbaum made changes -
          Description Original: As described in [https://stackoverflow.com/questions/61420959/jenkins-interpretation-of-multiple-object-declarations-on-one-line], CPS-transformed assignment expressions do not return their result. Here is an example:
          {code:java}
          def a = b = c = 1 {code}
          In a Pipeline, after executing that expression {{a == null}}, {{b == null}}, and {{c == 1}}.

          In regular Groovy, after executing that expression, {{a}}, {{b}}, and {{c}}, are all equal to 1.

          Here is a (failing) {{workflow-cps}} test case (for {{CpsFlowDefinition2Test}}) that demonstrates the bug:
          {code:java}
          @Test public void assignmentExprsReturnRhs() throws Exception {
              WorkflowJob p = jenkins.createProject(WorkflowJob.class);
              p.setDefinition(new CpsFlowDefinition(
                      "def a = b = c = 1\n" +
                      "println(/a == ${a}/)\n" +
                      "println(/b == ${b}/)\n" +
                      "println(/c == ${c}/)\n", true));
              WorkflowRun b = jenkins.buildAndAssertSuccess(p);
              jenkins.assertLogContains("a == 1", b);
              jenkins.assertLogContains("b == 1", b);
              jenkins.assertLogContains("c == 1", b);
          } {code}
          I think the relevant code is [AssignmentBlock|https://github.com/cloudbees/groovy-cps/blob/94939d33fab9b04be76b9fbd0477b9807bf1ad32/lib/src/main/java/com/cloudbees/groovy/cps/impl/AssignmentBlock.java] in {{groovy-cps}}. In particular, I think that [assignAndDone|https://github.com/cloudbees/groovy-cps/blob/94939d33fab9b04be76b9fbd0477b9807bf1ad32/lib/src/main/java/com/cloudbees/groovy/cps/impl/AssignmentBlock.java#L70] needs to create a new continuation to pass to {{lhs.set}} that calls {{k.receive(rhs)}} internally.
          New: As described in [https://stackoverflow.com/questions/61420959/jenkins-interpretation-of-multiple-object-declarations-on-one-line], CPS-transformed assignment expressions evaluate to {{null}} instead of the value of the RHS of the expression. Here is an example:
          {code:java}
          def a = b = c = 1 {code}
          In a Pipeline, after executing that expression {{a == null}}, {{b == null}}, and {{c == 1}}.

          In regular Groovy, after executing that expression, {{a}}, {{b}}, and {{c}}, are all equal to 1.

          Here is a (failing) {{workflow-cps}} test case (for {{CpsFlowDefinition2Test}}) that demonstrates the bug:
          {code:java}
          @Test public void assignmentExprsReturnRhs() throws Exception {
              WorkflowJob p = jenkins.createProject(WorkflowJob.class);
              p.setDefinition(new CpsFlowDefinition(
                      "def a = b = c = 1\n" +
                      "println(/a == ${a}/)\n" +
                      "println(/b == ${b}/)\n" +
                      "println(/c == ${c}/)\n", true));
              WorkflowRun b = jenkins.buildAndAssertSuccess(p);
              jenkins.assertLogContains("a == 1", b);
              jenkins.assertLogContains("b == 1", b);
              jenkins.assertLogContains("c == 1", b);
          } {code}
          I think the relevant code is [AssignmentBlock|https://github.com/cloudbees/groovy-cps/blob/94939d33fab9b04be76b9fbd0477b9807bf1ad32/lib/src/main/java/com/cloudbees/groovy/cps/impl/AssignmentBlock.java] in {{groovy-cps}}. In particular, I think that [assignAndDone|https://github.com/cloudbees/groovy-cps/blob/94939d33fab9b04be76b9fbd0477b9807bf1ad32/lib/src/main/java/com/cloudbees/groovy/cps/impl/AssignmentBlock.java#L70] needs to create a new continuation to pass to {{lhs.set}} that calls {{k.receive(rhs)}} internally.
          Devin Nusbaum made changes -
          Description Original: As described in [https://stackoverflow.com/questions/61420959/jenkins-interpretation-of-multiple-object-declarations-on-one-line], CPS-transformed assignment expressions evaluate to {{null}} instead of the value of the RHS of the expression. Here is an example:
          {code:java}
          def a = b = c = 1 {code}
          In a Pipeline, after executing that expression {{a == null}}, {{b == null}}, and {{c == 1}}.

          In regular Groovy, after executing that expression, {{a}}, {{b}}, and {{c}}, are all equal to 1.

          Here is a (failing) {{workflow-cps}} test case (for {{CpsFlowDefinition2Test}}) that demonstrates the bug:
          {code:java}
          @Test public void assignmentExprsReturnRhs() throws Exception {
              WorkflowJob p = jenkins.createProject(WorkflowJob.class);
              p.setDefinition(new CpsFlowDefinition(
                      "def a = b = c = 1\n" +
                      "println(/a == ${a}/)\n" +
                      "println(/b == ${b}/)\n" +
                      "println(/c == ${c}/)\n", true));
              WorkflowRun b = jenkins.buildAndAssertSuccess(p);
              jenkins.assertLogContains("a == 1", b);
              jenkins.assertLogContains("b == 1", b);
              jenkins.assertLogContains("c == 1", b);
          } {code}
          I think the relevant code is [AssignmentBlock|https://github.com/cloudbees/groovy-cps/blob/94939d33fab9b04be76b9fbd0477b9807bf1ad32/lib/src/main/java/com/cloudbees/groovy/cps/impl/AssignmentBlock.java] in {{groovy-cps}}. In particular, I think that [assignAndDone|https://github.com/cloudbees/groovy-cps/blob/94939d33fab9b04be76b9fbd0477b9807bf1ad32/lib/src/main/java/com/cloudbees/groovy/cps/impl/AssignmentBlock.java#L70] needs to create a new continuation to pass to {{lhs.set}} that calls {{k.receive(rhs)}} internally.
          New: As described in [https://stackoverflow.com/questions/61420959/jenkins-interpretation-of-multiple-object-declarations-on-one-line], CPS-transformed assignment expressions evaluate to {{null}} instead of the value of the RHS of the expression. Here is an example:
          {code:java}
          def a = b = c = 1 {code}
          In a Pipeline, after executing that expression {{a == null}}, {{b == null}}, and {{c == 1}}.

          In regular Groovy, after executing that expression, {{a}}, {{b}}, and {{c}}, are all equal to 1.

          Here is a (failing) {{workflow-cps}} test case (for {{CpsFlowDefinition2Test}}) that demonstrates the bug:
          {code:java}
          @Test public void assignmentExprsReturnRhs() throws Exception {
              WorkflowJob p = jenkins.createProject(WorkflowJob.class);
              p.setDefinition(new CpsFlowDefinition(
                      "def a = b = c = 1\n" +
                      "println([a, b, c].toString())", true));
              WorkflowRun b = jenkins.buildAndAssertSuccess(p);
              jenkins.assertLogContains("[1, 1, 1]", b); // Actual output today is "[null, null, 1]".
          } {code}
          I think the relevant code is [AssignmentBlock|https://github.com/cloudbees/groovy-cps/blob/94939d33fab9b04be76b9fbd0477b9807bf1ad32/lib/src/main/java/com/cloudbees/groovy/cps/impl/AssignmentBlock.java] in {{groovy-cps}}. In particular, I think that [assignAndDone|https://github.com/cloudbees/groovy-cps/blob/94939d33fab9b04be76b9fbd0477b9807bf1ad32/lib/src/main/java/com/cloudbees/groovy/cps/impl/AssignmentBlock.java#L70] needs to create a new continuation to pass to {{lhs.set}} that calls {{k.receive(rhs)}} internally.
          Devin Nusbaum made changes -
          Remote Link New: This issue links to "cloudbees/groovy-cps#108 (Web Link)" [ 24898 ]
          Devin Nusbaum made changes -
          Assignee New: Devin Nusbaum [ dnusbaum ]
          Devin Nusbaum made changes -
          Status Original: Open [ 1 ] New: In Progress [ 3 ]
          Devin Nusbaum made changes -
          Status Original: In Progress [ 3 ] New: In Review [ 10005 ]

          Justin Vallon added a comment -

          Variations, where assignment is used in conditionals:

          Object o;
          boolean looped = false;
          for (; o = "abc"; ) {
            looped = true;
            break;
          }
          assert looped; // should be true; CPS gives false
          
          boolean x;
          boolean q = false;
          while (x = true) {
              q = true;
              break;
          }
          assert q; // fails in CPS
          

          I see there is a merged PR.

          Justin Vallon added a comment - Variations, where assignment is used in conditionals: Object o; boolean looped = false ; for (; o = "abc" ; ) { looped = true ; break ; } assert looped; // should be true ; CPS gives false boolean x; boolean q = false ; while (x = true ) { q = true ; break ; } assert q; // fails in CPS I see there is a merged PR.

            dnusbaum Devin Nusbaum
            dnusbaum Devin Nusbaum
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: