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

Field references in CPS class methods fail in CPS subclasses

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

      Still trying to figure out how to describe this exactly. =)

      There are two cases that are related that I can reproduce. First, in a single Jenkinsfile:

      class C {
        int x = 33
        int getX() {
          2 * this.@x
        }
      }
      
      class D extends C { 
        int getY() { 
          2 * getX() } 
      }
      
      new D().y
      

      This results in groovy.lang.MissingFieldException: No such field: x for class: D, with the following stacktrace:

      	at groovy.lang.MetaClassImpl.getAttribute(MetaClassImpl.java:2823)
      	at groovy.lang.MetaClassImpl.getAttribute(MetaClassImpl.java:3759)
      	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getGroovyObjectField(ScriptBytecodeAdapter.java:356)
      	at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.getAttribute(DefaultInvoker.java:53)
      	at com.cloudbees.groovy.cps.impl.AttributeAccessBlock.rawGet(AttributeAccessBlock.java:20)
      	at C.getX(Script1.groovy:1)
      	at D.getY(Script1.groovy:1)
      	at Script1.run(Script1.groovy:1)
      

      The second I can't reproduce in a single Jenkinsfile - each of the inheriting classes need to be in a shared library, and it needs multiple levels of inheritance.

      class BaseAction {
        String someActionValue = "thevalue"
      }
      
      class ChildAction extends BaseAction {
          def doSomething() {
              return "in ChildAction: " + someActionValue
          }
      }
      
      class GrandChildAction extends ChildAction {
          def doSomething() {
              return "in GrandChildAction: " + someActionValue
          }
      }
      

      With the Jenkinsfile:

      @Library("config@master") _
      import ChildAction
      import GrandChildAction
      println new ChildAction().doSomething()
      println new GrandChildAction().doSomething()
      

      This results in groovy.lang.MissingFieldException: No such field: someActionValue for class: GrandChildAction with the following stacktrace:

      at groovy.lang.MetaClassImpl.getAttribute(MetaClassImpl.java:2846)
      	at groovy.lang.MetaClassImpl.getAttribute(MetaClassImpl.java:3782)
      	at org.codehaus.groovy.runtime.InvokerHelper.getAttribute(InvokerHelper.java:147)
      	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getField(ScriptBytecodeAdapter.java:306)
      	at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.getAttribute(DefaultInvoker.java:48)
      	at com.cloudbees.groovy.cps.impl.AttributeAccessBlock.rawGet(AttributeAccessBlock.java:20)
      	at GrandChildAction.doSomething(/private/var/folders/pr/24nv8g910wg8vr4b4c33q34r0000gn/T/jenkinsTests.tmp/jenkins6831451434103658873test/jobs/p/builds/1/libs/config/src/GrandChildAction.groovy:4)
      	at WorkflowScript.run(WorkflowScript:5)
      

      I left a comment detailing my investigation into this so far over on JENKINS-50736:

      What's actually happening is that we're actually calling (via some layers of abstraction) InvokerHelper.getAttribute(object, name), which ends up getting the MetaBeanProperty for that object/name combo. And the field field of that MetaBeanProperty is null. This is the same behavior you get in vanilla Groovy if you do this.@someField in the subclass. So the problem seems to be that you can't actually call InvokerHelper.getAttribute(object, name) for a field on a superclass, and our hacks to avoid recursion hell with getter methods (which I think was intended primarily to deal with explicit getters, not autogenerated getters, but don't quote me on that) will end up doing that.

      How to fix this? Well, I've got a couple hacks I've tried that seem to "fix" this for some value of "fixed" - most notably changing DefaultInvoker#getAttribute(lhs, name) to catch MissingFieldException when it shows up and try forcing a MetaClass#getAttribute call with InvokerHelper.getMetaClass(lhs.getClass().getSuperclass()), recursing on until we get to Object.class. But that feels really really dirty, so I'm trying to think of something better.

      So at a minimum, we're not properly handling references to fields as attribute expressions (i.e., this.@foo) in a superclass, even if the child class never actually references the field-as-attribute directly, just via a superclass method (note that it doesn't have to be in a getter method like getX to blow up - I used a different method name and it still went kerplooie).

      Needs more thought and explanation, but I wanted to give this a separate JIRA for clarity.

          [JENKINS-50954] Field references in CPS class methods fail in CPS subclasses

          There are no comments yet on this issue.

            Unassigned Unassigned
            abayer Andrew Bayer
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: