Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • Fate of JSR-241
  • Pattern matching (perhaps too much work to do before 1.7) — Guillaume Laforge
  • Discuss the design of a new Meta-Object Protocol
    • handling of access to private methods/fields/properties
    • general missing property/method handling (order of lookup, what extension points)
    • are invokeMethod and get/setProperty on GroovyObject still needed? What was their purpose in the first place and how did they develop?
    • How does EMC allow extension to invokeMethod and get/setProperty logics? (see for example http://groovy.codehaus.org/ExpandoMetaClass+-+GroovyObject+Methods) And should that be kept, or is it intelligent to do so?
    • How to isolate meta class registries? Is that a good thing, is it not needed? How does for example Ruby handle this?
    • How easily give any created and not yet created meta class a change? How to isolate such actions?
    • Having more information on call sites
    • more uniform method missing path
    • various scoping aspects
      • lexical or dynamic
      • block, class, package, OSGi
      • thread-bound or not
      • state tracing (stacked changes)
    • inheritance of meta-changes
    • fast
      • adaptable to invoke dynamic
      • avoid synchronization on data
      • relying on immutable data structures
      • change retrieval (transactionality?)
    • non leaking abstractions for the user
    • discovery of meta-changes
      • meta-inf/services
      • magic package
  • Some use cases
    Code Block
    
                                                                         
                                                                         
                                                                         
                                                 
    /*
    //add property to number
    class Distance {
      def number, name
      Distance(number,name) {
        this.name = name; this.number=number
      }
      String toString(){
        "$number $name"
      }
    }
    
    // current MOP
    Integer.metaClass.getKilometers = {->new Distance(delegate,"km")}
    println 5.kilometers
    
    //new MOP
    Integer.metaClass.getKilometers = {->new Distance(delegate,"km")}
    println 5.kilometers
    
    
    //intercept method
    class RemoteObject {
      def killServer() {"done."}
    }
    
    //current MOP
    def mc = RemoteObject.metaClass
    def old =mc.getMetaMethod("killServer",[] as Object[])
    mc.killServer = {->"you are kidding me!? ok... "+old.doMethodInvoke(delegate,[] as Object[])}
    def ro = new RemoteObject()
    println ro.killServer()
    
    //new MOP
    mc = RemoteObject.metaClass
    def old = mc.getMetaMethod("killServer")
    mc.killServer = {->"you are kidding me!? ok... "+old(delegate)}
    def ro = new RemoteObject()
    println ro.killServer()
    */
    
    class RemoteObject {
      def killServer() {"done."}
      def killYourself() {"no!"}
    }
    class MyIn implements Interceptor {
        Object beforeInvoke(Object object, String methodName, Object[] arguments){}
        Object afterInvoke(Object object, String methodName, Object[] arguments, Object result){
            "original $methodName: $result"
        }
        boolean doInvoke(){true}
    }
    
    def mc = RemoteObject.metaClass
    mc.methodMissing = {String name, args ->
        "let us "+name
    }
    def proxy = ProxyMetaClass.getInstance(RemoteObject)
    proxy.interceptor = new MyIn()
    //RemoteObject.metaClass = proxy
    def ro = new RemoteObject()
    ro.metaClass = proxy
    println ro.killServer()
    println ro.killYourself()
    println ro."kill all humans"()
    
    Code Block
                                                                                                         
    /*// EMC DSL (since 1.6.0)
    Object.metaClass {
       invokeMethod {}
    }
    
    // Groovy old way (since 1.0)
    class A implements GroovyInterceptable {
      def invokeMethod(String name, args){}
    }
    
    // EMC closure assignment way (since ~1.5)
    Object.metaClass.invokeMethod = {..}
    
    // alternative annotation based (not yet in)
    class A {
      @GroovyInterceptor
      private foo(String name, args){}
    }
    
    Object.metaClass().interceptableMethods = ["foo"]*/
    //ExpandoMetaClass.enableGlobally()
    /*
    def a = new ArrayList()
    println a.metaClass
    
    ArrayList.metaClass.myMethod2 = {-> println "hi" }
    println a.metaClass
    a.myMethod2()
    
    def b = new ArrayList()
    //b.metaClass.myMethod2 = {-> println "hi b" }
    b.myMethod2()
    println ArrayList.metaClass
    println b.metaClass
    */
    
  • issues with 'private'
    Code Block
    
    /*
    class A {
      final private m() {1}
      def getM(){m()}
      def getN(other){other.m()}
    }
    def a = new A()
    assert a.getM()==1
    assert a.getN(a)==1
    
    class B extends A {
      public m(){2}
    }
    def b = new B()
    assert b.getM()==1
    assert b.m() == 2
    assert b.getN(a)==1
    assert b.getN(b)==2
    */
    
    class A {
      private String foo() {"1"}
      def bar(){foo()}
    }
    def a = new A()
    assert a.bar()=="1"
    
    a.metaClass.foo = {-> 2 }
    assert a.bar()==2
    a.foo() // ok
    
    a.metaClass = null
    assert a.bar()=="1"
    a.foo() // exception
    
    /*'
    class B extends A {
      Integer foo() {2}
    }
    def b = new B()
    assert b.bar()=="1"
    
    B.declaredMethods.findAll{it.name=="foo"}.each {println it}
    */
    println "done."
    
    
    
    proxy#getValue(String name)
    
    proxy.metaClass.getProperty = {proxy,name-> proxy.getValue(name)};
    change(proxy)
    proxy.foo.bar.else
    

Notes

  • Mocking support is lacking and would need an overhaul in 1.7
  • Check for coverage support results validity
  • Discussion on extended annotations
    Code Block
    @Validator({ name.size > 3})
    String name
    
    @InRange(18..65)
    int age
    
    @Regex(~/\d{5}/)
    String zipCode
    
    @SEF def action = { println "foo" } // OK
    @SEF { println "foo" } // NO!
    
    @SEF callSideEffectMethod(a, b, c)
    
    • Idea of encoding in the form of Strings: would create a script, which could support ranges, closures, regex. Try first with Strings to see how far we can go.
    • If such annotations can be reused from Java, what's up with parameter names, field access, etc?
    • Two aspects: adding annotations support on the various AST nodes, and how to encode that into bytecode
  • Regarding loosening the omission of parens, we've currently got a good compromise on readability

...