Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-6001

Method overloading behavior is non-deterministic and differs from Java behavior

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Blocker
    • Resolution: Fixed
    • Affects Version/s: 2.1.0
    • Fix Version/s: 2.1.2
    • Component/s: class generator, groovy-jdk
    • Labels:
      None
    • Environment:

      Description

      This example is based on a real world example. I've included blank interface and class implementations to simulate WebSphere's class hierarchy.

      If you run the attached example (bug.groovy) repeatedly (CTRL+ENTER is easiest) in Groovy Console you will sometimes see this output:

      FooApplication.onRequestStart(HttpServletRequest, HttpServletResponse)

      And other times this output:

      BazApplication.onRequestStart(PortletRequest, PortletResponse)
      FooApplication.onRequestStart(HttpServletRequest, HttpServletResponse)

      Or this:
      BazApplication.onRequestStart(PortletRequest, PortletResponse
      FooApplication.onRequestStart(PortletRequest, PortletResponse)

      Here's a subset of the code in question

      class FooApplication extends Application implements HttpServletRequestListener, PortletRequestListener{
      
          void onRequestStart(HttpServletRequest request, HttpServletResponse response) {
              System.out.println("FooApplication.onRequestStart(HttpServletRequest, HttpServletResponse)");
          }
      
          void onRequestEnd(HttpServletRequest request, HttpServletResponse response) {
              System.out.println("FooApplication.onRequestEnd(HttpServletRequest, HttpServletResponse)");
          }
      
          void onRequestStart(PortletRequest request, PortletResponse response) {
              System.out.println("FooApplication.onRequestStart(PortletRequest, PortletResponse)");
          }
      
          void onRequestEnd(PortletRequest request, PortletResponse response) {
              System.out.println("FooApplication.onRequestEnd(PortletRequest, PortletResponse)");
          }
          
      }
      
      class BazApplication extends FooApplication {
      
          void onRequestStart(PortletRequest request, PortletResponse response) {
              println 'BazApplication.onRequestStart(PortletRequest, PortletResponse'
              super.onRequestStart(request, response);
          }
      
      }
      
      

      Note that ResourceRequestImpl implements both HttpServletRequest and PortletRequest but PortletRequest is implemented closer in the class hierarchy. Granted this test case is a bit odd but comes from real-world example in a portlet environment running groovy with 3rd party code (IBM WebSphere) we don't control.

      As you can see the method being selected at runtime is non-deterministic. I'm no language expert but my assumption is that given a ResourceRequestImpl instance that groovy runtime should call BazApplication.onRequestStart(PortletRequest, PortletResponse) and FooApplication.onRequestStart(PortletRequest, PortletResponse) when super is invoked.

      A pure Java implementation behaves according to my assumption. If BazAppication is converted to Java it works every time (see BarApplication and BarApplicationTest in bug.zip archive). The assumption being that onRequestStart(PortletRequest request, PortletResponse response) should be called as it is the most specific method.

      I've attached a gradle build script which implements both groovy (BazApplication) and java versions (BarApplication) which demonstrates that Java works according to my assumptions but the groovy one behaves non-deterministically:

      $ gradle clean test

      Gradle test > com.textura.BarApplicationTest.test STANDARD_OUT
      BarApplication.onRequestStart(PortletRequest, PortletResponse)
      FooApplication.onRequestStart(PortletRequest, PortletResponse)

      Gradle test > com.textura.BazApplicationTest.test STANDARD_OUT
      FooApplication.onRequestStart(HttpServletRequest, HttpServletResponse)

      Repeated runs BazApplicationTest yields different results:

      Gradle test > com.textura.BazApplicationTest.test STANDARD_OUT
      BazApplication.onRequestStart(PortletRequest, PortletResponse
      FooApplication.onRequestStart(PortletRequest, PortletResponse)

      Gradle test > com.textura.BazApplicationTest.test STANDARD_OUT

      BazApplication.onRequestStart(PortletRequest, PortletResponse)
      FooApplication.onRequestStart(HttpServletRequest, HttpServletResponse)

      A co-worker of mine sent this white paper titled "Reconciling Method Overloading and Dynamically Typed Scripting Languages" that you might find interesting.

      https://docs.google.com/viewer?a=v&q=cache:La7ci5TSnFwJ:https://upapers.dcc.uchile.cl/index/publications/view_pdf/7979+&hl=en&gl=us&pid=bl&srcid=ADGEESiZMldG2xzVwAC7nUfDpmGkAA-brSfHaTAOQq4vfoZUoTCrUShcegjdmWJFsihVpyRT92duVIgDptelMCc--sy3RCotbH9c0OQC7PuVf2D90fYYXVoyru5KmsOxQrZXSkhX6ZUe&sig=AHIEtbSK5HwtBD8Z_nhYk2du3HzZBY_rFg

      Maybe I'm mistaken but assuming what I said before about how Java resolves which method to call is accurate I don't understand why Groovy wouldn't be able to walk the tree to determine the most specific interface which should be selected in the same manner as the Java compiler. I also tried using @StaticCompile but it had no effect AFAIK.

        Attachments

        1. bug.groovy
          2 kB
          Kirk Rasmussen
        2. ResourceRequestImpl.png
          26 kB
          Kirk Rasmussen
        3. bug.zip
          10 kB
          Kirk Rasmussen

          Activity

            People

            • Assignee:
              blackdrag Jochen Theodorou
              Reporter:
              krasmussen Kirk Rasmussen
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: