groovy
  1. groovy
  2. GROOVY-5359

static propertyMissing catches inherited static methods

    Details

    • Type: Improvement Improvement
    • Status: Open Open
    • Priority: Minor Minor
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: groovy-runtime
    • Labels:
      None
    • Testcase included:
      yes
    • Number of attachments :
      0

      Description

      When static missing properties are catched, the handler is called also when an inherited static method is called.

      It is not a serious problem, but it feels weird. Either it should be changed (if possible with reasonably small effort), or thoroughly documented.

      Also, it should be documented that if the handler throws a MissingPropertyException, it gets silently eaten up and the static method is called all right. On the other hand, any other exception goes all the way up to the user-level harness if any, which is generally rather undesirable.

      Here's a code sample which illustrates the problem, along with the proper exception thrown so as the method is properly called:

      class Foo {
       static def inheritedStaticMethod() {
         println "Allright, Kilroy's here"
       }
      }
      
      class Test extends Foo {
        static def main(av) {
          // need to catch all class-level missing properties
          Object.metaClass.static.propertyMissing={
            println "Property $it of ${delegate.simpleName} missing."
            // simulated 'found valid dynamic property' case
            if (it.startsWith('f')) return "(${delegate.simpleName}.$it OK)"
            // oops, we did not find valid d.p, so we 
            throw new MissingPropertyException("No valid DP for ${delegate.simpleName}.$it")
          }
          println "Checking the handler: ${Object.foo} ${Foo.foo} ${Test.foo} ${String.foo}"
          inheritedStaticMethod()
        }
      }
      

        Activity

        Hide
        OC added a comment -

        Oh, I did not notice – there remains one problem, for which I don't see an easy work-around.

        If it is possible that there are both a property and a static class method of the same name, they are, far as I can see, undistinguishable in the propertyMissing handler.

        Suppose the class Foo of the example should also have a dynamic property named "inheritedStaticMethod". Far as I can say, for a class is having both a static method and property of the same name completely legal and valid case.

        It will fail with potentially disastrous consequences, for the propertyMissing code would find the dynamic property at wrong moment (when it was not accessed in fact). If the dynamic property access happens to have any side-effects (even as innocent as filling caches), all hell may break loose.

        Seems I cannot edit the issue, but perhaps someone who can might bump it up to priority "Major", unless an easy work-around for this problem exists and I am just overlooking it? Thanks.

        Show
        OC added a comment - Oh, I did not notice – there remains one problem, for which I don't see an easy work-around. If it is possible that there are both a property and a static class method of the same name, they are, far as I can see, undistinguishable in the propertyMissing handler. Suppose the class Foo of the example should also have a dynamic property named "inheritedStaticMethod". Far as I can say, for a class is having both a static method and property of the same name completely legal and valid case. It will fail with potentially disastrous consequences, for the propertyMissing code would find the dynamic property at wrong moment (when it was not accessed in fact). If the dynamic property access happens to have any side-effects (even as innocent as filling caches), all hell may break loose. Seems I cannot edit the issue, but perhaps someone who can might bump it up to priority "Major", unless an easy work-around for this problem exists and I am just overlooking it? Thanks.
        blackdrag blackdrag made changes -
        Field Original Value New Value
        Component/s groovy-runtime [ 16250 ]
        Hide
        OC added a comment -

        And there's another problem – the bug's a big performance killer. Having profiled my code, I've eventually found one of main causes of slowness is precisely this, i.e., that when my code calls inherited static methods, the Groovy dispatcher goes through the static propertyMissing hook which has to throw and the Groovy dispatcher catches the exception.

        246 /tmp> <t.groovy
        class t {
          static main(av) {
            ExpandoMetaClass.enableGlobally()
            Foo.metaClass.static.propertyMissing={name->
              //println "static property missing $name in $delegate"
              throw new MissingPropertyException("- static method called -")
            }
            measure(1,'encache f') { Foo.inheritedMethod() }
            measure(1,'encache b') { Bar.inheritedMethod() }
            measure(1e6,'f has static propMissing') { Foo.inheritedMethod() }
            measure(1e6,'b does not have it') { Bar.inheritedMethod() }
            measure(1e6,'f try again') { Foo.inheritedMethod() }
            measure(1e6,'b again much faster') { Bar.inheritedMethod() }
          }
          static measure(repeat,String what,Closure block) {
            def start=new Date().getTime()
            repeat.times(block)
            println "$what: took ${new Date().getTime()-start} ms"
          }
        }
        class Root {
          static inheritedMethod() { }
        }
        class Foo extends Root {
        }
        class Bar extends Root {
        }
        247 /tmp> groovy t
        encache f: took 60 ms
        encache b: took 11 ms
        f has static propMissing: took 15905 ms
        b does not have it: took 6467 ms
        f try again: took 14918 ms
        b again much faster: took 6496 ms
        248 /tmp> 
        
        Show
        OC added a comment - And there's another problem – the bug's a big performance killer. Having profiled my code, I've eventually found one of main causes of slowness is precisely this, i.e., that when my code calls inherited static methods, the Groovy dispatcher goes through the static propertyMissing hook which has to throw and the Groovy dispatcher catches the exception. 246 /tmp> <t.groovy class t { static main(av) { ExpandoMetaClass.enableGlobally() Foo.metaClass. static .propertyMissing={name-> //println " static property missing $name in $delegate" throw new MissingPropertyException( "- static method called -" ) } measure(1,'encache f') { Foo.inheritedMethod() } measure(1,'encache b') { Bar.inheritedMethod() } measure(1e6,'f has static propMissing') { Foo.inheritedMethod() } measure(1e6,'b does not have it') { Bar.inheritedMethod() } measure(1e6,'f try again') { Foo.inheritedMethod() } measure(1e6,'b again much faster') { Bar.inheritedMethod() } } static measure(repeat, String what,Closure block) { def start= new Date().getTime() repeat.times(block) println "$what: took ${ new Date().getTime()-start} ms" } } class Root { static inheritedMethod() { } } class Foo extends Root { } class Bar extends Root { } 247 /tmp> groovy t encache f: took 60 ms encache b: took 11 ms f has static propMissing: took 15905 ms b does not have it: took 6467 ms f try again: took 14918 ms b again much faster: took 6496 ms 248 /tmp>
        Pascal Schumacher made changes -
        Description When static missing properties are catched, the handler is called also when an inherited static method is called.

        It is not a serious problem, but it feels weird. Either it should be changed (if possible with reasonably small effort), or thoroughly documented.

        Also, it should be documented that if the handler throws a MissingPropertyException, it gets silently eaten up and the static method is called all right. On the other hand, any other exception goes all the way up to the user-level harness if any, which is generally rather undesirable.

        Here's a code sample which illustrates the problem, along with the proper exception thrown so as the method is properly called:

        ===
        class Foo {
         static def inheritedStaticMethod() {
           println "Allright, Kilroy's here"
         }
        }
        class Test extends Foo {
          static def main(av) {
            // need to catch all class-level missing properties
            Object.metaClass.static.propertyMissing={
              println "Property $it of ${delegate.simpleName} missing."
              // simulated 'found valid dynamic property' case
              if (it.startsWith('f')) return "(${delegate.simpleName}.$it OK)"
              // oops, we did not find valid d.p, so we
              throw new MissingPropertyException("No valid DP for ${delegate.simpleName}.$it")
            }
            println "Checking the handler: ${Object.foo} ${Foo.foo} ${Test.foo} ${String.foo}"
            inheritedStaticMethod()
          }
        }
        ===
        When static missing properties are catched, the handler is called also when an inherited static method is called.

        It is not a serious problem, but it feels weird. Either it should be changed (if possible with reasonably small effort), or thoroughly documented.

        Also, it should be documented that if the handler throws a MissingPropertyException, it gets silently eaten up and the static method is called all right. On the other hand, any other exception goes all the way up to the user-level harness if any, which is generally rather undesirable.

        Here's a code sample which illustrates the problem, along with the proper exception thrown so as the method is properly called:

        {code}
        class Foo {
         static def inheritedStaticMethod() {
           println "Allright, Kilroy's here"
         }
        }

        class Test extends Foo {
          static def main(av) {
            // need to catch all class-level missing properties
            Object.metaClass.static.propertyMissing={
              println "Property $it of ${delegate.simpleName} missing."
              // simulated 'found valid dynamic property' case
              if (it.startsWith('f')) return "(${delegate.simpleName}.$it OK)"
              // oops, we did not find valid d.p, so we
              throw new MissingPropertyException("No valid DP for ${delegate.simpleName}.$it")
            }
            println "Checking the handler: ${Object.foo} ${Foo.foo} ${Test.foo} ${String.foo}"
            inheritedStaticMethod()
          }
        }
        {code}

          People

          • Assignee:
            Unassigned
            Reporter:
            OC
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated: