From commits-return-7602-archive-asf-public=cust-asf.ponee.io@groovy.apache.org Tue Oct 30 07:20:19 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id C92AC180791 for ; Tue, 30 Oct 2018 07:20:16 +0100 (CET) Received: (qmail 74489 invoked by uid 500); 30 Oct 2018 06:20:15 -0000 Mailing-List: contact commits-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list commits@groovy.apache.org Received: (qmail 74178 invoked by uid 99); 30 Oct 2018 06:20:14 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 30 Oct 2018 06:20:14 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 835F5E0A27; Tue, 30 Oct 2018 06:20:14 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: paulk@apache.org To: commits@groovy.apache.org Date: Tue, 30 Oct 2018 06:20:18 -0000 Message-Id: In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [05/15] groovy-dev-site git commit: initial commit http://git-wip-us.apache.org/repos/asf/groovy-dev-site/blob/c2f50e52/wiki/GEP-10.html ---------------------------------------------------------------------- diff --git a/wiki/GEP-10.html b/wiki/GEP-10.html new file mode 100644 index 0000000..deafb00 --- /dev/null +++ b/wiki/GEP-10.html @@ -0,0 +1,531 @@ + + + + + + The Apache Groovy programming language - Developer docs - GEP-10 + +
+ + Fork me on GitHub + +
+
+
+
+

GEP-10

Author:


+
+
+
+
Metadata
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Number + +

GEP-10

+
+Title + +

Static compilation

+
+Version + +

4

+
+Type + +

Feature

+
+Status + +

Final

+
+Comment + +

Delivered in Groovy 2.0 and enhanced in later versions

+
+Leader + +

Cédric Champeau

+
+Created + +

2011-11-23

+
+Last modification  + +

2018-10-26

+
+
+
+
+
+
+
+

Abstract: Static compilation

+
+
+

This GEP introduces an experimental new feature in the language known as static compilation. +Static compilation can be used when the dynamic features of Groovy are not necessary and that the +performance of the dynamic runtime is too low. The reader must understand that :

+
+
+
    +
  • +

    we do not plan to change the semantics of regular Groovy

    +
  • +
  • +

    we want static compilation to be triggered explicitly

    +
  • +
  • +

    we want the semantics of static groovy to be as close as possible as the semantics of dynamic Groovy

    +
  • +
+
+
+

Rationale: Static Type Checking vs Static compilation

+
+

Static compilation heavily relies on another feature called Static type checking but it is important to understand +that they are separate steps. You can statically type check your code, and still have the dynamic runtime at work. +Static type checking adds type inference to the compilation process. This means that, for example, type arguments +of method calls are inferred, as well as return types of closures or methods. Those inferred types are used by the +checker to verify the type flow of your program. When it does so, once must be aware that the static checker cannot +behave like dynamic Groovy. This means that even if a program passes static type checking, it may behave differently at runtime.

+
+
+
+

Method selection

+
+

Method selection in dynamic Groovy

+
+

Groovy supports multimethods. Especially, in dynamic Groovy, target methods are chosen at runtime, +in opposite to Java which chooses target method at compile time. Let’s illustrate the difference with an example :

+
+
+
+
public void foo(String arg) { System.out.println("String"); }
+public void foo(Object o) { System.out.println("Object"); }
+
+Object o = "The type of o at runtime is String";
+foo(o);
+
+
+
+

In Java, this code will output "Object", because the target method is chosen according to the type of the arguments at compile time. +The declared type of "o" is Object, so the Object version of the method is chosen. If you want to call the String version, +you have to define "o" as a "String", or cast o to string in the method call arguments. Now the same example in dynamic Groovy :

+
+
+
+
void foo(String arg) { println 'String' }
+void foo(Object o) { println 'Object' }
+
+def o = 'The type of o at runtime is String'
+foo(o)
+
+
+
+

If you run this snippet, you will see that Groovy prints "String". The reason is that Groovy resolves type arguments at +runtime and chooses the most specialized version of a method signature when multiple versions are possible. +To deal with multiple arguments, Groovy computes a distance between the actual type arguments and the declared +argument types of a method. The method with the best score is chosen.

+
+
+

The method selection process is complex, and makes Groovy quite powerful. It is also possible, for example, +to define metaclasses which will choose different methods, or change the return type of a method dynamically. +This behaviour is responsible for a large part of the poor performance of Groovy invocation times as compared to Java. +However, performance has greatly improved with the introduction of call site caching in Groovy. +Even with those optimizations, Groovy is far for the performance of a pure static language.

+
+
+
+

InvokeDynamic

+
+

InvokeDynamic support is under development and should be introduced in the upcoming Groovy 2.0. +What InvokeDynamic allows us to do is to, basically, replace call site caching with a native "dynamic method dispatch" +system directly implemented by the JVM. It is still uncertain what performance improvement we will reach. +At best, we could be close to the performance of a statically typed language. +However, as some features are still difficult to implement with InvokeDynamic, +most likely the performance gain will not be so high. It is important to talk about it though, because :

+
+
+
    +
  • +

    invokedynamic allows JIT optimizations

    +
  • +
  • +

    replaces the current dynamic method dispatch without changing the semantics of the language

    +
  • +
  • +

    if performance is good, could make static compilation unnecessary

    +
  • +
+
+
+

However, InvokeDynamic has a major drawback : it will only be available for people using a Java 7+ JVM. +If we want to deliver Java-like performance for Groovy programs to our users, can we afford leaving +most of them without such? Most probably, Java 7 won’t be mainstream before two or three years.

+
+
+

Rémi Forax, however, created a backport of invokedynamic for older versions of the JVM. +Such a backport relies on bytecode transformation to replace invokedynamic instructions with "emulated instructions". +This "emulated" mode is great for us Groovy developers, because it would allow us to write code once and run it on +any JVM, but for users, the performance would most probably be bad. To be sure of that, an experiment with backported +InDy will be made once invokedynamic support is implemented in Groovy core. The most probable situation, +though, is that performance of such code will remain far from what a static language could provide.

+
+
+
+
+

Static Groovy

+
+

Type inference based dispatch

+
+

This GEP is there to experiment a static compilation mode in Groovy. With what we have explained before, +you should already understand that statically typed code implies a different behaviour from dynamic code. +If you expect the statically checked and statically compiled code to behave exactly like dynamic Groovy, +you should already stop there, or wait for invoke dynamic support to expect improved performance. +If you perfectly understand that statically compiled code means different semantics, +then you can continue reading this GEP, and help us choose the best implementation path. +In fact, there are several options that we will explain here.

+
+
+

The current implementation relies on the static type checker, which performs type inference. +This means that with the previous example :

+
+
+
+
void foo(String arg) { println 'String' }
+void foo(Object o) { println 'Object' }
+
+def o = 'The type of o at runtime is String'
+foo(o)
+
+
+
+

The compiler is able to infer that when the foo method is called, the actual type argument will be a string. +If we compile it statically, the behaviour of this statically compiled program at runtime will be the same as dynamic Groovy. +With such an implementation, we expect most programs to behave statically like they would in dynamic Groovy. +However, this will never be always true. This is why we say this behaviour is "as close as possible" as the one of dynamic Groovy.

+
+
+

The drawback of this implementation is that the developer cannot easily know what method the compiler chooses. +For example, let’s take this example, extracted from a discussion on the mailing list:

+
+
+
+
void foo(String msg) { println msg }
+void foo(Object msg) { println 'Object' }
+
+def doIt = {x ->
+  Object o = x
+  foo(o)
+}
+
+def getXXX() { "return String" }
+
+def o2=getXXX()
+doIt o2   // "String method" or "Object method"????
+
+
+
+

The static type checker infers the type of o2 from the return type of getXXX, so knows that doIt is called with a String, +so you could suspect the program to choose the foo(String) method. However, doIt is a closure, which can therefore +be reused in many places, and the type of its "x" argument is unknown. The type checker will not generate distinct +closure classes for the different call sites where it is used. This means that when you are in the closure, +the type of 'x' is the one declared in the closure arguments. This means that without type information on 'x', +x is supposed an Object, and the foo method which will be statically chosen will be the one with the Object argument.

+
+
+

While this can be surprising, this is not really difficult to understand. To behave correctly, +you must either add explicit type arguments to the closure, which is always preferred. +In a word, in a statically checked world, it is preferred to limit the places where types will be inferred so +that code is understood properly. Even if you don’t do it, fixing code is easy, so we think this is not a major issue.

+
+
+
+

Java-like method dispatch

+
+

The alternative implementation is not to rely on inferred types, but rather behave exactly like Java does. +The main advantage of this is that the user doesn’t have 3 distinct method dispatch modes to understand, +like in the previous solution (Java, dynamic Groovy, inferred static Groovy). +The major drawback is that the semantics of static Groovy are not close to the ones of dynamic Groovy. +For this reason, this is not the preferred experimental implementation. If you think this version should be preferred, +do not hesitate to send an email to the mailing list so that we can discuss. +You can even fork the current implementation to provide your own.

+
+
+
+
+

Testing

+
+

Static compilation is now part of the Groovy 2.0.0 release. You can download the latest Groovy 2 releases and test it.

+
+
+

@CompileStatic

+
+

You can try the following snippet :

+
+
+
+
@groovy.transform.CompileStatic
+int fib(int i) {
+    i < 2 ? 1 : fib(i - 2) + fib(i - 1)
+}
+
+
+
+

This code should already run as fast as Java.

+
+
+
+

The "arrow" operator for direct method calls

+
+

Some users have suggested another idea, related to static compilation, which is the "arrow operator". +Basically, the idea is to introduce another way of calling methods in Groovy :

+
+
+
+
obj.method() // dynamic dispatch
+obj->method() // static dispatch
+
+
+
+

While this idea sounds interesting, especially when you want to mix dynamic and static code in a single method, +we think it has many drawbacks. First, it introduces a grammar change, something we would like to avoid as much as possible. +Second, the idea behind this operator is to perform direct method calls when you know the type of an object. +But, without type inference, you have two problems :

+
+
+
    +
  • +

    even if the type of 'obj' is specified, you cannot be sure that at runtime, the type will be the same

    +
  • +
  • +

    you still have to infer the argument types too, which leaves us with the same problem as before: relying on type inference, +or relying on Java-like behaviour where the method is chosen based on the declared types. +If we do so, then we would introduce possible incompatibility with the static mode…​ +So we would have to choose between this mode and the full static compilation mode.

    +
  • +
+
+
+

Imagine the following code :

+
+
+
+
void write(PrintWriter out) {
+   out->write('Hello')
+   out->write(template())
+}
+
+def template() { new MarkupBuilder().html { p('Hello') } }
+
+
+
+

While the first call can be resolved easily, this is not the same with the second one. +You would have to rewrite your code probably this way to have this work :

+
+
+
+
void write(PrintWriter out) {
+   out->println('Hello')
+   String content = template() // dynamic call, implicit coercion to string
+   out->println(content) // static method dispatch based on declared types only
+}
+
+def template() { new MarkupBuilder().html { p('Hello') } }
+
+
+
+

Which is not necessary if you use the @CompileStatic annotation :

+
+
+
+
@CompileStatic
+void write(PrintWriter out) {
+   out.println('Hello')
+   out.println(template())
+}
+
+def template() { new MarkupBuilder().html { p('Hello') } }
+
+
+
+
+
+
+
+ +
+ +
+

Mailing-list discussions

+
+ +
+
+
+

JIRA issues

+ +
+
+
+
+

Update history

+
+
+
+
3 (2012-06-21)
+
+

Version as extracted from Codehaus wiki

+
+
4 (2018-10-26)
+
+

Numerous minor tweaks

+
+
+
+
+
+
+
+
+
+ \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy-dev-site/blob/c2f50e52/wiki/GEP-11.html ---------------------------------------------------------------------- diff --git a/wiki/GEP-11.html b/wiki/GEP-11.html new file mode 100644 index 0000000..97ae9b6 --- /dev/null +++ b/wiki/GEP-11.html @@ -0,0 +1,632 @@ + + + + + + The Apache Groovy programming language - Developer docs - GEP-11 + +
+ + Fork me on GitHub + +
+
+
+
+

GEP-11

Author:


+
+
+
+
Metadata
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Number + +

GEP-11

+
+Title + +

Groovy 3 semantics and new MOP

+
+Version + +

4

+
+Type + +

Feature

+
+Status + +

Draft

+
+Leader + +

Jochen "blackdrag" Theodorou

+
+Created + +

2012-06-26

+
+Last modification  + +

2018-10-28

+
+
+
+
+
+
+
+

Abstract: Groovy 3 semantics and new MOP

+
+
+

For quite long we are thinking about a new MOP for Groovy, without finding the right way to do it better this time. +It is difficult to find the right path between being user-friendly and efficient. +Thus the new MOP will contain some features the old did not, but will also remove some old features for +the sake of a more straight and more easy to understand MOP.

+
+
+
+
+

Rationale

+
+
+

Removing default null argument

+
+

The default null argument is used for method calls that have one parameter, +but the call is done without an argument for the parameter. +Groovy will here use null, as long as the type of the parameter is not a primitive. +In case of a primitive the call fails. This feature was mainly added in the early Groovy pre 1.0 years +before Groovy supported default values for parameters. Another limitation of this logic is that it works +only with single parameter methods. There have been multiple cases of confused users by this logic. +Performance wise this logic doesn’t cost much, and certainly not more than a call with a real argument. +But since this is a confusing feature of limited use, it should be removed.

+
+
+
+

Removing automatic list expansion

+
+

A method call done with a list that finds no matching method for that list (a method with one parameter of type List, +Collection, Object, etc), will cause a second method selection iteration. This time the list is "unpacked" and all +elements of the list are taken as if the method call had been done with the elements rather than the list. +Groovy also supports spreading of lists by an syntax element, making this automatic feature not needed. +In fact this can be quite surprising for users and is a problem for performance. +A spread version might be still not good in performance, but at least the user will have +to use an extra symbol and thus have the visual indicator. As of why this feature was originally added is unclear. +Looking at user code you will find barely intended usages of this. Thus it should be removed.

+
+
+
+

Changing Safe Navigation to stop evaluation

+
+

Currently an expression like a?.b.c will fail if a is null. It will not evaluate b, but it will try to evaluate c on null. +This defies the intend of safe navigation to avoid a NullPointerException. Thus this should be changed to stop the +evaluation of the path expression.

+
+
+
+

User Wish List

+
+
    +
  • +

    Instance based Categories by Wujek Srujek
    +Instead of having to provide a class with static methods it would be good to be able to feed an instance to the +use-construct and that then we will use this instance along with its instance methods. This allows instance state to be used.

    +
  • +
  • +

    Replacing Introspector by blackdrag
    +Not only because of bugs like GROOVY-5019 there should be a replacement of the Introspector

    +
  • +
+
+
+
+

Some of the leading design ideas of this MOP

+
+

The plan is to orientate us a lot on the open call site caching the JVM provides with invokedynamic. +For this to work all parts of the MOP should no longer be seen as places that do invoke something, +but as places that return something, that then will be invoked. An invokeMethod then will for example +return instead an object that acts as a kind of handler, which can be invoked. +Groovy will then store it and avoid the reselection unless you invalidate it. +In the old MOP such caching often did not happen once you interact using meta programming. +The tasks to be solved in this are to provide an "extension point" for intercepting methods and to react to +missing methods, as well as being able to invalidate a cached version and/or to make an uncached version possible.

+
+
+
+

MOP2 outline

+
+

image

+
+
+

in meta class of x in pseudo code:

+
+
+
+
cachable = getMethodInterceptor(...)
+if (cachable==null) cachable = getFittingMethod(...)
+if (cachable==null) getMethodMissingHandler(...)
+if (cachable==null) throw new MethodMissingException(...)
+storeInCallSite(cachable)
+invoke(cachable)
+
+
+
+

as an explanation to the methods:

+
+
+
    +
  • +

    getMethodInterceptor
    +Extension point for the user to register an interceptor, that will be used for all method calls. +This is equal what GroovyInterceptable does using invokeMethod, only that in MOP1 the method is directly called. +In MOP2 we will instead return a handler that then will do whatever is needed. This can for example be a method +handle to a invokeMethod method if wanted. TODO: define guards

    +
  • +
  • +

    getFittingMethod
    +Predefined way to select a method through meta class. This method will return an +object which then can be used to invoke the selected method.

    +
  • +
  • +

    getMethodMissingHandler
    +Extension point for the user to register a handler for missing methods. +The then invoked method is comparable to what methodMissing in MOP1 would be doing.

    +
  • +
  • +

    MethodMissingException
    +thrown if none of the above methods is bearing a non null result

    +
  • +
  • +

    storeInCallSite
    +Predefined method to store the cachable result of the above process in the call site. +As long as the cached object is valid the above methods will not be called again for this call site.

    +
  • +
  • +

    invoke
    +Predefined method to do the Initial invocation of the cachable. Subsequent invocation may be done directly by invokedynamic.

    +
  • +
+
+
+
+

Context Meta Class

+
+

For the MOP2 Groovy will use a system of meta classes with a context element and an always existing default. +At each call site only one such view will be valid and it will be constant. +Those views can be used to defined "sealed" meta class, which won’t get influenced by outside +meta classes or to allow for example calling private methods and not allowing them in other cases. +This makes 3 possible meta class variations currently:

+
+
+
    +
  • +

    Default meta class
    +Behaves pretty much like what Java allows in regards of method calls (same class private methods are available) +and all meta programming changes are visible

    +
  • +
  • +

    Sealed meta class
    +Like the default meta class, but meta programming changes from outside the class are not visible. +This can be especially useful to library writers.

    +
  • +
  • +

    Test meta class (un)sealed
    +Like the two before but with the addition that private members will always be available.

    +
  • +
+
+
+
+

Getting a Meta Class

+
+

Because of the concept of context a class does not have one direct meta class that can be generated without its context. +The call site defines the place of the context. How the context itself is defined is a TODO. +As an implementation strategy it is possible to for example use ClassValue to store a table with the context being a key. +The key would probably have to be available as static information, or as easily computable information. +Since the resulting meta class could be stored later in the call site object context changes are to be avoided, +since it implies the invalidation of the call sites using that context.

+
+
+
+

General dispatch rule for methods (and properties)

+
+

To define the dispatch rules correctly we need to define some terms first:
+Static Sender Class (SSC): This is the static information about the class a call is made from. +If there is for example a class A and a class B extends A, and a call in a method in A, +then even if your instance is actually a B the SSC will still be A.
+Inheritance based Multimethods (short multimethods from now on): Given a class A and a class B extends A, +a call made from within A may see a method defined on B as long as the method is visible (not private). +Groovy defines a special exception to this though. If the method call from within A is calling a method of the name m, +then a m from B can only be visible if there is no private m defined in A.

+
+
+

Given those two definitions a method call in A will select the set of method to decide from based on this: +A call m() with the SSC A and done on an instance of B (extends A) will be using the methods defined in A, if A has a private m, otherwise the call is done using B.

+
+
+

Calls to Super:
+A call to super in B extends A will have the SSC B, but for the method selection process the super class of SSC (super(SSC)) will be used. In super calls mutimethods are not visible. Thus we can directly use the meta class super(SSC), but we will dispatch only on the public methods of that meta class.

+
+
+
+

Module Extension Methods Shadowing Rules

+
+

Module Extensions Methods are in the old and new MOP defined by the DefaultGroovyMethods related classes and module extension, like groovy-swing. In the definition here we will use the terms of from "inside" and from "outside" to define a callsite, that lies in the same class as the target method (inside) or not (outside). The general rules are:

+
+
+
    +
  • +

    public methods are shadowed

    +
  • +
  • +

    private methods are shadowed for outside callsites, but not for inside callsites

    +
  • +
+
+
+

Subclasses of the class the module extension method has been applied to have these extended rules:

+
+
+
    +
  • +

    if the subclass defines a private method of the same signature as the module extension method, then outside callsites will still see the extension method, inside callsites the private method

    +
  • +
  • +

    A call to "super" or "this" will call the module extension method. As such the subclass is seen as outside callsite.

    +
  • +
+
+
+

Open Blocks are not seen as separate classes.

+
+
+
+

Property Discovery

+
+

Currently MetaClass discovers properties based on the Java Beans conventions. +It also allows pseudo properties matching a convention on java.beans.EventSetDescriptor. +This allows the following trick in SwingBuilder for example:

+
+
+
+
button(actionPerformed: { println it })
+
+
+
+

The pseudo property actionPerformed is inferred from the single method exposed by ActionListener, +a type of listener that can be registered on a JButton. The code responsible for discovering these +properties is buried in MetaClassImpl and is not accessible to the outside. +It would be great if this mechanism be made pluggable.

+
+
+
+

The Realm concept

+
+

In MOP2 a Realm is a tree like structure containing the meta classes. +There is a root realm, used as default, but there can be any number of lower realms. +A meta class change is visible in a realm, if the change is done to the meta class in +the same realm or to a meta class in a higher realm. +Script execution engines are able to set a realm for example to prevent them changing meta classes they should not change. +This can be used for unit tests to isolate meta class changes done during the tests as well. +A library can have its own realm (defined through an annotation) to prevent other classes to +leak their changes into that library, while the library can still use a higher realm to make +changes more public visible, if the realm allows that. Realms can have a setting that prevents +code executed from there to make changes to higher realms. Calling a method is always done using +the meta classes from the current realm, even if the called class then calls other classes using +its own realm. A realm is thus not thread local structure, it is more of a lexical scope. +A realm can also use a different meta class flavor, to for example allow access to private methods and fields.

+
+
+
+

Work Items

+
+

This part is to guide the implementors with the course of action and planning of the subtasks.

+
+
+
    +
  • +

    make indy the only compilation target in the build

    +
  • +
  • +

    move all non-indy bytecode interfacing code to a module, which may be removed later. This includes ScriptBytecodeAdpater as well as all the custom call site caching classes

    +
  • +
  • +

    make a new module for MOP2

    +
  • +
  • +

    turn meta class into an immutable

    +
  • +
  • +

    implement meta class views

    +
  • +
+
+
+
+

Breaking changes trace

+
+

groovy.lang.MetaObjectProtocol (currently in groovy.mop.MetaObjectProtocol):

+
+
+
    +
  • +

    getProperties() renamed to getMetaProperties()

    +
  • +
  • +

    getMethods() renamed to getMetaMethods()

    +
  • +
  • +

    respondsTo(Object, String, Object[]) changed to respondsTo(String, Object…​)

    +
  • +
  • +

    respondsTo(Object, String) replaced by getMetaMethods(String, Class…​) with the class argument being null

    +
  • +
  • +

    hasProperty(Object,String) replaced by getMetaProperty(String) being null or not

    +
  • +
  • +

    getStaticMetaMethod(String, Object[]) replaced by respondsTo(String, Object…​) and inspecting the list for static methods

    +
  • +
  • +

    getMetaMethod(name, Object[]) replaced by respondsTo(String, Object…​) in case the arguments are no classes and getMetaMethods(String,Class…​) in case of the arguments being classes

    +
  • +
  • +

    invokeConstructor(Object[])NO REPLACEMENT

    +
  • +
  • +

    invokeMethod(Object, String, Object[]) NO REPLACEMENT

    +
  • +
  • +

    invokeMethod(Object, String, Object) NO REPLACEMENT

    +
  • +
  • +

    invokeStaticMethod(Object, String, Object[]) NO REPLACEMENT

    +
  • +
  • +

    getProperty(Object, String) replaced by MetaProperty#invoke

    +
  • +
  • +

    setProperty(Object, String, Object) replaced by MetaProperty#invoke

    +
  • +
  • +

    getAttribute(Object, String) replaced by MetaProperty#getField#invoke

    +
  • +
  • +

    setAttribute(Object, String, Object) replaced by MetaProperty#getField#invoke

    +
  • +
  • +

    getMetaProperty(String), getTheClass() UNCHANGED

    +
  • +
+
+
+

groovy.lang.MetaMethod is split into a public interface groovy.mop.MetaMethod and an internal default implementation groovy.mop.internal.DefaultMetaMethod.

+
+
+

Differences to groovy.mop.internal.DefaultMetaMethod:

+
+
+
    +
  • +

    does no longer extend ParameterTypes and does no longer implement Clonable

    +
  • +
  • +

    no protected fields nativeParamTypes, parameterTypes and isVargsMethod

    +
  • +
  • +

    the constructor MetaMethod() and MetaMethod(Class[]) are removed and partially replaced by DefaultMetaMethod(Class, String, int, MethodHandle) and DefaultMetaMethod(Class, String, MethodType), which uses the MethodType or the MethodHandle to define the parameter classes

    +
  • +
  • +

    coerceArgumentsToClasses(Object[]), correctArguments(Object[]), isValidExactMethod(Class[]), isValidExactMethod(Object[]), isValidMethod(Class[]), isValidMethod(Object[]), isVargsMethod(), isVargsMethod(Object[]) NO REPLACEMENT

    +
  • +
  • +

    getNativeParameterTypes()replaced by getParameterClasses()

    +
  • +
  • +

    equal(CachedClass[], CachedClass[]), equal(CachedClass[], Class[]), checkParameters(Class[]), clone(), doMethodInvoke(Object, Object[]), getDescriptor() NO REPLACEMENT

    +
  • +
  • +

    getDeclaringClass(), getModifiers(), getName(), getReturnType(), isAbstract(), isPrivate(), isProtected(), isPublic(), isStatic(), toString() UNCHANGED

    +
  • +
  • +

    getMopName(), getSignature(), invoke(Object, Object[]), isCacheable(), isMethod(MetaMethod), isSame(MetaMethod), processDoMethodInvokeException(Exception, Object, Object[]) NO REPLACEMENT

    +
  • +
+
+
+

groovy.lang.MetaProperty is split into a public interface groovy.mop.MetaProperty and an internal default implementation groovy.mop.internal.DefaultMetaProperty.

+
+
+

Differences to groovy.mop.internal.DefaultMetaProperty:

+
+
+
    +
  • +

    the public static field PROPERTY_SET_PREFIX is removed NO REPLACEMENT

    +
  • +
  • +

    the protected fields name and type are now private and have to be requested through getName and getType

    +
  • +
  • +

    getModifiers(), getName(), getType(), DefaultMetaProperty(String, Class) UNCHANGED

    +
  • +
  • +

    getGetterName(String, Class), getSetterName(String)NO REPLACEMENT

    +
  • +
  • +

    getProperty(Object) replaced by getter(boolean)

    +
  • +
  • +

    setProperty(Object, Object) replaced by setter(boolean)

    +
  • +
+
+
+
+
+
+ +
+
+ +
+
+

Mailing-list discussions

+ +
+
+

Reference implementation

+
+ +
+
+
+
+
+

Update history

+
+
+
+
3 (2013-10-11)
+
+

Version as extracted from Codehaus wiki

+
+
4 (2018-10-28)
+
+

Numerous minor tweaks

+
+
+
+
+
+
+
+
+
+ \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy-dev-site/blob/c2f50e52/wiki/GEP-12.html ---------------------------------------------------------------------- diff --git a/wiki/GEP-12.html b/wiki/GEP-12.html new file mode 100644 index 0000000..6a9bad3 --- /dev/null +++ b/wiki/GEP-12.html @@ -0,0 +1,513 @@ + + + + + + The Apache Groovy programming language - Developer docs - GEP-12 + +
+ + Fork me on GitHub + +
+
+
+
+

GEP-12

Author:


+
+
+
+
Metadata
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+Number + +

GEP-12

+
+Title + +

SAM coercion

+
+Version + +

4

+
+Type + +

Feature

+
+Status + +

Final

+
+Leader + +

Jochen "blackdrag" Theodorou

+
+Created + +

2013-05-30

+
+Last modification  + +

2018-10-29

+
+
+
+
+
+
+
+

Abstract: SAM coercion

+
+
+

SAM stands for Single Abstract Method. SAM coercion is here a Groovy style transformation of a +groovy.lang.Closure instance into an object suitable for our SAM type. +A SAM type is an abstract class or interface with a single abstract method. The coercion can happen as part of an assignment or as the result of a method call. Since this transformation might be outside of the types provided by Closure itself, it can be more than a simple Java style cast. Closure becomes a kind of sub type to all SAM types. Groovy has other such transformations without explicit cast or asType usage, which are number object transformations as well as the conversion of GString to String.

+
+
+

Motivation

+
+

Even before Java8 we had discussions about supporting different interfaces with Closure like Runnable and Callable. +These two being easy cases, any framework can define a myriad of interfaces and abstract classes. +This then requires to "groovify" the library by writing a helper layer capable of transforming Closure +objects into something the library then understand. While it is unlikely of this approach to make Groovy +Builder surplus, it can still help with a more simple integration.

+
+
+

Meaning of "Single Abstract Method"

+
+

For a SAM type to be a SAM type according to this GEP an abstract class or interface with a single +abstract method is required. Any static methods, as well as non-abstract methods are not counted. +The abstract method may be defined in the SAM class itself, but it can also be a parent. +The required visibility modifier for the method is public though.

+
+
+

SAM examples:

+
+
+
    +
  • +

    Simple Interface:

    +
  • +
+
+
+
+
interface SAM {
+  def foo()
+}
+
+
+
+
    +
  • +

    Interface with defender method (aka virtual extension method):

    +
  • +
+
+
+
+
interface SAM {
+  def foo()
+  def bar() default { 1 }
+}
+
+
+
+
    +
  • +

    Interface inheriting from another interface:

    +
  • +
+
+
+
+
interface ParentOfSAM {}
+interface SAM extends ParentOfSAM {
+  def foo()
+}
+
+
+
+
    +
  • +

    Interface inheriting from another interface, but not defining a method on its own:

    +
  • +
+
+
+
+
interface ParentOfSAM {
+  def foo()
+}
+interface SAM extends ParentOfSAM {}
+
+
+
+
    +
  • +

    simple abstract class

    +
  • +
+
+
+
+
abstract class SAM {
+  abstract foo()
+}
+
+
+
+
    +
  • +

    abstract class with a abstract and a non abstract method:

    +
  • +
+
+
+
+
abstract class SAM {
+  abstract foo()
+  def bar() { 1 }
+}
+
+
+
+
    +
  • +

    abstract class extending other class:

    +
  • +
+
+
+
+
abstract class ParentOfSAM1 {
+  abstract foo()
+}
+
+
+
+
    +
  • +

    abstract class SAM1 extends ParentOfSAM1 {}

    +
  • +
+
+
+
+
class ParentOfSAM {
+   def bar() { 1 }
+}
+abstract class SAM2 extends  {
+  abstract foo()
+}
+
+
+
+
    +
  • +

    abstract class implementing interface:

    +
  • +
+
+
+
+
interface SomeInterface{
+  def foo()
+}
+abstract class SAM1 implements SomeInterface {}
+abstract class SAM2 implements Runnable{}
+interface AnotherInterface {}
+abstract class SAM3 implements AnotherInterface {
+  abstract foo()
+}
+
+
+
+

Non-SAM examples:

+
+
+
    +
  • +

    empty interface:

    +
  • +
+
+
+
+
interface SomeMarker {}
+
+
+
+
    +
  • +

    interface with two methods:

    +
  • +
+
+
+
+
interface TwoMethods {
+  def foo()
+  def bar()
+}
+
+
+
+
    +
  • +

    abstract class with two abstract methods:

    +
  • +
+
+
+
+
abstract class TwoMethods {
+  abstract foo()
+  abstract bar()
+}
+
+
+
+
    +
  • +

    empty abstract class:

    +
  • +
+
+
+
+
abstract class Empty {}
+
+
+
+
+

Influence on method selection

+
+

The normal method selection algorithm tries to find the most specific method to the given argument +runtime types and the most general for null. Since a SAM type and a target method parameter type +are not in an inheritance relation "most specific" needs a redefinition in parts. +It will be assumed the SAM type is like a direct child of the given target type, +but if the SAM type is one implemented by Closure (Runnable and Callable), +then no SAM coercion will be needed. This case is preferred in method selection. +In case of an overloaded method, where each can be used as target for the SAM coercion, +method selection will thus fail, regardless their internal relation. +In Groovy the actual method signature of the SAM type and the coercion target are not important. +Also it is not important if the target type is an abstract class or an interface.

+
+
+

Example of two SAM targets with failing runtime method selection:

+
+
+
+
interface SAM1 { def foo(String s)}
+interface SAM2 { def bar(Integer i)}
+def method(x, SAM1 s1){s1.foo(x)}
+def method(x, SAM2 s2){s2.bar(x)}
+method (1)   {it}  // fails because SAM1 and SAM2 are seen as equal
+method ("1") {it}  // fails because SAM1 and SAM2 are seen as equal
+
+
+
+

Example of SAM type being ignore as a non-coercion case is available:

+
+
+
+
interface SAM {def foo(String s)}
+def method(SAM s) {1}
+def method(Runnable r) {2}
+assert method {it} == 2
+
+
+
+
+

Influence on static typing system

+
+

The Scope for the static type system is split into a basic part for Groovy 2.2 and an extended one +for a later version (2.3 or 3.0)

+
+
+
+

Groovy 2.2 static checks

+
+

The type checking in Groovy 2.2 will be limited to mimic the behavior of normal Groovy. +No method signature checks are performed, as well as there will be no additional test or method +selection based on the type provided by the open block.

+
+
+
+

Groovy 2.2+ static checks

+
+

In later versions of Groovy the static type checker has to be improved to refine method selection by the given type +signature through the open block or lambda. A SAM type is then a fitting type for the coercion only if the provided +types and the target types in the SAM are matching by number and type itself. A more detailed description can be +found here: http://cr.openjdk.java.net/~dlsmith/jsr335-0.6.1/F.html

+
+
+
+
+
+
+ +
+
+ +
+
+

Reference implementation

+
+ +
+
+ +
+
+
+

Update history

+
+
+
+
3 (2013-07-01)
+
+

Version as extracted from Codehaus wiki

+
+
4 (2018-10-29)
+
+

Numerous minor tweaks

+
+
+
+
+
+
+
+
+
+ \ No newline at end of file