How does Groovy register Closure Listener on JButton - groovy

I'm learning Groovy and I've seen this example:
button = new JButton('Push me!')
button.actionPerformed = { event ->
println button.text
}
There is no actionPerformed field/method on JButton....
Could someone explain how Closure is being registered by Groovy on actionPerformed ?

TL;DR
JButton metaclass registers 31 listeners methods (one of them is actionPerformed) and whenever you call button.actionPerformed = { event -> } Groovy executes a method like setProperty(object, field, value) which checks if there is a listener registered with a given field name - if does, it executes registered listener method (javax.swing.AbstractButton.addActionListener(java.awt.event.ActionListener) in this case).
Detailed explanation
Groovy uses MOP (metaobject protocol) for a dynamic runtime environment. It means that Groovy does not call methods directly like in Java, but uses this additional layer instead. It allows changing class behavior at a runtime.
Whenever we try to set a class field/property value like
button.actionPerformed = { event -> println "Clicked!" }
Groovy calls proper setProperty method. In case of a setting a property for class like JButton, following setProperty method gets called:
https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/MetaClassImpl.java#L2602
/**
* <p>Retrieves a property on the given receiver for the specified arguments. The sender is the class that is requesting the property from the object.
* The MetaClass will attempt to establish the method to invoke based on the name and arguments provided.
*
* <p>The useSuper and fromInsideClass help the Groovy runtime perform optimisations on the call to go directly
* to the super class if necessary
*
* #param sender The java.lang.Class instance that is mutating the property
* #param object The Object which the property is being set on
* #param name The name of the property
* #param newValue The new value of the property to set
* #param useSuper Whether the call is to a super class property
* #param fromInsideClass Whether the call was invoked from the inside or the outside of the class.
*/
public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
checkInitalised();
//----------------------------------------------------------------------
// handling of static
//----------------------------------------------------------------------
boolean isStatic = theClass != Class.class && object instanceof Class;
if (isStatic && object != theClass) {
MetaClass mc = registry.getMetaClass((Class) object);
mc.getProperty(sender, object, name, useSuper, fromInsideClass);
return;
}
// .....
//----------------------------------------------------------------------
// listener method
//----------------------------------------------------------------------
boolean ambiguousListener = false;
if (method == null) {
method = listeners.get(name);
ambiguousListener = method == AMBIGUOUS_LISTENER_METHOD;
if (method != null &&
!ambiguousListener &&
newValue instanceof Closure) {
// let's create a dynamic proxy
Object proxy = Proxy.newProxyInstance(
theClass.getClassLoader(),
new Class[]{method.getParameterTypes()[0].getTheClass()},
new ConvertedClosure((Closure) newValue, name));
arguments = new Object[]{proxy};
newValue = proxy;
} else {
method = null;
}
}
// ......
}
It goes to the block responsible for checking listener methods. Swing related classes register their listener methods so you can add a listener method like:
button.actionPerformed = { event -> .... }
instead of
button.addActionListener(new ActionListener() {
#Override
void actionPerformed(ActionEvent actionEvent) {
}
})
And here is the list of all 31 registered listeners:
Keys are the names of listeners and values are method objects that receives a closure set to a property. Of course it finds a listener method for a key actionPerformed - it gets a reference to a method
public void javax.swing.AbstractButton.addActionListener(java.awt.event.ActionListener)
and it passes a closure
{ event ->
println button.text
}
to it.
When these listeners like actionPerformed get registered to a metaclass?
Metaclass object gets initialized whenever you call a class constructor. Groovy in this case calls
org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(Object receiver, Object arg1, Object arg2)
https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/org/codehaus/groovy/runtime/callsite/AbstractCallSite.java#L245
If you follow call stack you will find these two major checkpoints:
at some point dynamic call constructor method reaches CallSiteArray.createCallConstructorSite() method that creates a metaclass object https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/org/codehaus/groovy/runtime/callsite/CallSiteArray.java#L86
MetaClassImpl.initialize() method calls addProperties() which sets up metaclass with e.g. listeners https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/MetaClassImpl.java#L3303
Inside MetaClassImpl.addProperties() Groovy lists all listener methods using BeanInfo class and registers all found listeners https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/MetaClassImpl.java#L3343
The full call stack to this MetaClassImpl.addProperties() method from IntelliJ IDEA debugger window looks like this:
You can set a breakpoint at any of these lines if you would like to dig even deeper. Hope it helps.

Related

Groovy - interceptor of ProxyMetaClass does not affect inner methods calls

The code snippet is from the book < Groovy in action 2nd >, with minor modifications.
1 this code works as expected
package test
class InspectMe {
int outer(){
return inner()
}
int inner(){
return 1
}
}
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
inspectMe.metaClass = proxyMetaClass
inspectMe.outer()
println(tracer.writer.toString())
output:
before test.InspectMe.outer()
before test.InspectMe.inner()
after test.InspectMe.inner()
after test.InspectMe.outer()
2 but this code's output is different
package test
class InspectMe {
int outer(){
return inner()
}
int inner(){
return 1
}
}
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
proxyMetaClass.use(inspectMe){
inspectMe.outer()
}
println(tracer.writer.toString())
output:
before test.InspectMe.outer()
after test.InspectMe.outer()
It seems TracingInterceptor dosen't intercept inner methods in the second code.
Maybe it's normal behavior, But it seems to me like a bug.
Can somebody please explain this?
I don't know if this is a bug or not, but I can explain why this different behavior happens. Let's start with analyzing what InspectMe.outer() method implementation looks like at the bytecode level (we decompile .class file):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.BytecodeInterface8;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
public class InspectMe implements GroovyObject {
public InspectMe() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
public int outer() {
CallSite[] var1 = $getCallSiteArray();
return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? this.inner() : DefaultTypeTransformation.intUnbox(var1[0].callCurrent(this));
}
public int inner() {
CallSite[] var1 = $getCallSiteArray();
return 1;
}
}
As you can see, the outer() method tests the following predicate
!__$stMC && !BytecodeInterface8.disabledStandardMetaClass()
and if it evaluates to true, it invokes directly this.inner() method avoiding Groovy's MOP (meta-object protocol) layer (no metaclass involved in this case). Otherwise, it invokes var1[0].callCurrent(this) which means that inner() method gets invoked through Groovy's MOP with metaclass and interceptor involved in its execution.
The two examples you have shown in the question present a different way of setting metaclass field. In the first case:
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
inspectMe.metaClass = proxyMetaClass // <-- setting metaClass with DefaultGroovyMethods
inspectMe.outer()
println(tracer.writer.toString())
we are invoking inspectMe.setMetaClass(proxyMetaClass) method using Groovy's MOP layer. This method gets added to InspectMe class by DefaultGroovyMethods.setMetaClass(GroovyObject self, MetaClass metaClass).
Now, if we take a quick look at how this setMetaClass method is implemented we will find something interesting:
/**
* Set the metaclass for a GroovyObject.
* #param self the object whose metaclass we want to set
* #param metaClass the new metaclass value
* #since 2.0.0
*/
public static void setMetaClass(GroovyObject self, MetaClass metaClass) {
// this method was introduced as to prevent from a stack overflow, described in GROOVY-5285
if (metaClass instanceof HandleMetaClass)
metaClass = ((HandleMetaClass)metaClass).getAdaptee();
self.setMetaClass(metaClass);
disablePrimitiveOptimization(self);
}
private static void disablePrimitiveOptimization(Object self) {
Field sdyn;
Class c = self.getClass();
try {
sdyn = c.getDeclaredField(Verifier.STATIC_METACLASS_BOOL);
sdyn.setBoolean(null, true);
} catch (Throwable e) {
//DO NOTHING
}
}
It invokes at the end private method disablePrimitiveOptimization(self). This method is responsible for assigning true to __$stMC class field (the constant Verifier.STATIC_METACLASS_BOOL stores __$stMC value). What does it mean in our case? It means that the predicate in outer() method:
return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? this.inner() : DefaultTypeTransformation.intUnbox(var1[0].callCurrent(this));
evaluates to false, because __$stMC is set to true. And in this case inner() method gets executed via MOP with metaClass and interceptor.
OK, but it explains the first case that works as expected. What happens in the second case?
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
proxyMetaClass.use(inspectMe){
inspectMe.outer()
}
println(tracer.writer.toString())
Firstly, we need to check what does proxyMetaClass.use() look like:
/**
* Use the ProxyMetaClass for the given Closure.
* Cares for balanced setting/unsetting ProxyMetaClass.
*
* #param closure piece of code to be executed with ProxyMetaClass
*/
public Object use(GroovyObject object, Closure closure) {
// grab existing meta (usually adaptee but we may have nested use calls)
MetaClass origMetaClass = object.getMetaClass();
object.setMetaClass(this);
try {
return closure.call();
} finally {
object.setMetaClass(origMetaClass);
}
}
It's pretty simple - it replaces metaClass for the time of closure execution and it sets the old metaClass back when closure's execution completes. Sounds like something similar to the first case, right? Not necessarily. This is Java code and it invokes object.setMetaClass(this) method directly (the object variable is of type GroovyObject which contains setMetaClass method). It means that the field __$stMC is not set to true (the default value is false), so the predicate in outer() method has to evaluate:
BytecodeInterface8.disabledStandardMetaClass()
If we run the second example we will see that this method call returns false:
And that is why the whole expression
!__$stMC && !BytecodeInterface8.disabledStandardMetaClass()
evaluates to true and the branch that invokes this.inner() directly gets executed.
Conclusion
I don't know if it was intended or not, but as you can see dynamic setMetaClass method disables primitive optimizations and continues using MOP, while ProxyMetaClass.use() sets the metaClass keeping primitive optimizations enabled and caused a direct method call. I guess this example shows a corner case no one thought about when implementing ProxyMetaClass class.
UPDATE
It seems like the difference between these two methods exists because ProxyMetaClass.use() was implemented in 2005 for Groovy 1.x and it got updated for the last time in 2009. This __$stMC field was added in 2011 and the DefaultGroovyMethods.setMetaClass(GroovyObject object, Closure cl) was introduced in 2012 according to its javadoc that says this method is available since Groovy 2.0.

modify script variable from a Closure in Groovy

I am trying to modify a script variable from inside a closure in a function. The problem can be distilled down to this:
#groovy.transform.Field int myField = 0
incrementField()
assert myField == 1
def incrementField() {
1.times { myField++ }
}
I think the problem has something to do with closure delegates, but I cannot quite wrap my head around the docs.
This behavior is caused by groovy.lang.Script class and the fact that it overrides following methods:
Object getProperty(String property)
void setProperty(String property, Object newValue)
Closure you have shown in the example uses delegate set to a script object and that's why both overridden methods get executed when you try to access or modify field defined in a script.
Now let's see what happens when your example reaches closure
{ myField++ }
Firstly, getProperty("myField") is called to return a value associated with this property. This method is implemented as:
public Object getProperty(String property) {
try {
return binding.getVariable(property);
} catch (MissingPropertyException e) {
return super.getProperty(property);
}
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Script.java#L54
binding object contains only one variable in the beginning - closure's args array. If we take a look at implementation of binding.getVariable(property) method we will see:
public Object getVariable(String name) {
if (variables == null)
throw new MissingPropertyException(name, this.getClass());
Object result = variables.get(name);
if (result == null && !variables.containsKey(name)) {
throw new MissingPropertyException(name, this.getClass());
}
return result;
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Binding.java#L56
In our case MissingPropertyException is being thrown, so Script.getProperty(property) method returns a value of field myField defined in our Groovy script - 0. Then Groovy increments this value by 1 and tries to set this new value to a field myField. In this case Script.setProperty(property, value) is being called:
public void setProperty(String property, Object newValue) {
if ("binding".equals(property))
setBinding((Binding) newValue);
else if("metaClass".equals(property))
setMetaClass((MetaClass)newValue);
else
binding.setVariable(property, newValue);
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Script.java#L62
As you can see it sets this new value using bindings object. If we display binding.variables we will see that now this internal map contains two entries: args -> [:] and myField -> 1. It explains why assertion in your script always fails. Body of the closure you have defined never reaches myField field from the script class.
Workaround
If you are not satisfied with the fact that Script class overrides setProperty(property, value) method you can always override it by hand in your script and use same implementation as GroovyObjectSupport.setProperty(property, value). Simply add below method to your Groovy script:
#Override
void setProperty(String property, Object newValue) {
getMetaClass().setProperty(this, property, newValue)
}
Now closure defined in incrementField will set a new value to a class field instead of to a bindings object. Of course it may cause some weird side effects, you have to be aware of that. I hope it helps.
Found a possible solution, using closure delegate:
#groovy.transform.Field def stuff = [
myField : 0
]
incrementField()
assert stuff.myField == 1
def incrementField() {
def body = { myField++ }
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = stuff
1.times body
}

On closures and groovy builder pattern

Starting to grasp closures in general and some groovy features.
Given the following code:
class Mailer {
void to(final String to) { println "to $to" }
void from(final String from) { println "from $from" }
static void send(Closure configuration) {
Mailer mailer = new Mailer()
mailer.with configuration
}
}
class MailSender {
static void sendMessage() {
Mailer.send {
to 'them'
from 'me'
}
}
}
MailSender.sendMessage()
What happens under the hood when you pass a closure to Mailer.send method?
Does to and from are passed as arguments from the Closure point of view? Which types the Closure maps them?
And then inside the Mailer.send method at the moment the Mailer object calls mailer.with receiving the configuration object, the object maps them into method calls. Groovy does this by reflection?
Groovy can dynamically define the delegate of a closure and even the this object.
with is setting the delegate and executing the closure. This is a verbose way to achieve the same:
def math = {
given 4
sum 5
print
}
class PrintMath {
def initial
def given(val) {
initial = val
}
def sum(val) {
initial += val
}
def getPrint() {
println initial
return initial
}
}
math.delegate = new PrintMath()
math.resolveStrategy = Closure.DELEGATE_ONLY
assert math() == 9
What happens under the hood when you pass a closure to Mailer.send method?
It receives a not-yet-executed block of code.
Does to and from are passed as arguments from the Closure point of view?
No, it is better thinking of them as an anonymous class/lambda in java, or a function(){} in javascript.
Which types the Closure maps them?
None, they are method calls waiting to be executed. They can be delegated to different objects, though.
And then inside the Mailer.send method at the moment the Mailer object calls mailer.with receiving the configuration object, the object maps them into method calls. Groovy does this by reflection?
You can decompile a Groovy class file to see what is going on. IIRC, Groovy currently uses a "reflector" strategy (with an arrayOfCallSite caching) to make calls faster OR it can use invokedynamic.
The closure math in the code above will result in this class:
// .. a lot of techno-babble
public Object doCall(Object it) {
CallSite[] arrayOfCallSite = $getCallSiteArray();
arrayOfCallSite[0].callCurrent(this, Integer.valueOf(4));
arrayOfCallSite[1].callCurrent(this, Integer.valueOf(5));
return arrayOfCallSite[2].callGroovyObjectGetProperty(this);
return null;
}

How to restore metaclass on object to original class definition

I've been trying to create a TEMPORARY override on new objects, and then to remove the override on the objects themselves. I'm not sure if this can be done, but here is what I've tried so far.
// Say I have a class like:
class Validator {
boolean validate() { println "code here to return actual true/false"; false }
}
// I have two integration points one of them is Here before construction:
// First integration point:
// Save actual validate function
def realValidate = Validator.&validate
// Make new instances of Validator have the validate function hardwired to true
Validator.metaClass.validate { -> println "hardwired true"; true }
// Code I'd rather not modify
// Now some code executes which news up an instance and calls validate
def validator = new Validator()
validator.validate() // This correctly calls our override
// Second integration point.
// Without newing up a new Validator object, I'd like to remove the override.
Validator.metaClass = null
validator.metaClass.validate = Validator.&validate
// This throws "java.lang.IllegalArgumentException: object is not an instance of declaring class"
//validator.validate()
// So maybe I have to explicitly say:
realValidate.resolveStrategy = Closure.DELEGATE_FIRST
// But this still throws the same exception
//validator.validate()
// Perhaps if I tell my objects metaclass to forget about validate, it will bubble up and look for the method on its declaring class?
validator.metaClass.validate = { -> throw new MissingMethodException("validate", Validator.class, (Object[])[], false) }
// This throws MissingMethodException: No signature of method: Validator.validate() is applicable for argument types: () values: []
// Possible solutions: validate(), wait()
//validator.validate()
Apologies for not having a super specific question, since I don't know what all is possible in this particular area. I'd love both the reason why my code doesn't work, as well as alternatives to make it work.
This could be a per instance meta class problem... Validator.metaClass = null will set the global meta class for the Validator class to default. but your validator instance here is a Groovy class and thus stores a separate reference to the meta class in the instance itself. Calls with that instance will not go through a lookup of the global meta class and instead use the per instance meta class (the reference stored in the instance itself). Thus validator.metaClass = null is the only way to reset this
A small modification to your strategy would be fruitful. Use metaClass on the object instead of the Class.
// Say I have a class like:
class Validator {
boolean validate() { println "code here to return actual true/false"; false }
}
def validator = new Validator()
// mark that the pointer is on object instead of class
def realValidate = validator.&validate
validator.metaClass.validate { -> println "hardwired true"; true }
validator.validate() // This correctly calls our override
// Second integration point.
// DO NOT NEED THIS
// validator.metaClass = null
// Assign the method pointer to validate to call original validate
validator.metaClass.validate = realValidate
validator.validate()
Your approach did not work because you had validate() overridden on the metaClass of Class reference instead of the object itself.

Using DynamicObject (IDynamicMetaObjectProvider) as a component of a static type leads to infinite loop

I'm trying to create a dynamic object that can be used as a component of a static object. Here is a contrived example of what I'm trying to accomplish.
Here is the dynamic component:
public class DynamicComponent : DynamicObject
{
public override bool TryInvokeMember(
InvokeMemberBinder binder,
object[] args,
out object result)
{
result = "hello";
return true;
}
}
And here is a class where inheriting from DynamicObject isn't an option...assume that there is some third party class that I'm forced to inherit from.
public class AStaticComponent : VendorLibraryClass, IDynamicMetaObjectProvider
{
IDynamicMetaObjectProvider component = new DynamicComponent();
public DynamicMetaObject GetMetaObject(Expression parameter)
{
var result = component.GetMetaObject(parameter);
return result;
}
}
The direct usage of DynamicComponent works:
dynamic dynamicComponent = new DynamicComponent();
Assert.AreEqual(dynamicComponent.AMethod(), "hello");
However, forwarding the GetMetaObject through AStaticComponent causes some form of an infinite loop.
dynamic dynamicComponent = new AStaticComponent();
Assert.AreEqual(dynamicComponent.AMethod(), "hello"); //causes an infinite loop
Anyone know why this occurs?
And if it's some baked in behavior of DynamicObject that I cannot change, could someone provide some help on how to create a IDynamicMetaObjectProvider from scratch to accomplish a component based dynamic object (just something to get things started)?
I think the problem is that the Expression parameter passed to GetMetaObject represents the target of the dynamic invocation (i.e. the current object). You are passing the outer object to the call on component.GetMetaObject, so the returned meta object is trying to resolve the call to AMethod on the outer object instead of itself, hence the infinite loop.
You can create your own meta object which delegates to the inner component when binding member invocations:
public class AStaticComponent : VendorLibraryClass, IDynamicMetaObjectProvider
{
IDynamicMetaObjectProvider component = new DynamicComponent();
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new DelegatingMetaObject(component, parameter, BindingRestrictions.GetTypeRestriction(parameter, this.GetType()), this);
}
private class DelegatingMetaObject : DynamicMetaObject
{
private readonly IDynamicMetaObjectProvider innerProvider;
public DelegatingMetaObject(IDynamicMetaObjectProvider innerProvider, Expression expr, BindingRestrictions restrictions)
: base(expr, restrictions)
{
this.innerProvider = innerProvider;
}
public DelegatingMetaObject(IDynamicMetaObjectProvider innerProvider, Expression expr, BindingRestrictions restrictions, object value)
: base(expr, restrictions, value)
{
this.innerProvider = innerProvider;
}
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
var innerMetaObject = innerProvider.GetMetaObject(Expression.Constant(innerProvider));
return innerMetaObject.BindInvokeMember(binder, args);
}
}
}
#Lee's answer is really useful, I wouldn't have known where to get started without it. But from using it in production code, I believe it has a subtle bug.
Dynamic calls are cached at the call site, and Lee's code produces a DynamicMetaObject which effectively states that the inner handling object is a constant. If you have a place in your code where you call a dynamic method on an instance of AStaticObject, and later the same point in the code calls the same method on a different instance of AStaticObject (i.e. because the variable of type AStaticObject now has a different value) then the code will make the wrong call, always calling methods on the handling object from the first instance encountered at that place in the code during that run of the code.
This is a like-for-like replacement, the key difference being the use of Expression.Field to tell the dynamic call caching system that the handling object depends on the parent object:
public class AStaticComponent : VendorLibraryClass, IDynamicMetaObjectProvider
{
IDynamicMetaObjectProvider component = new DynamicComponent();
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new DelegatingMetaObject(parameter, this, nameof(component));
}
private class DelegatingMetaObject : DynamicMetaObject
{
private readonly DynamicMetaObject innerMetaObject;
public DelegatingMetaObject(Expression expression, object outerObject, string innerFieldName)
: base(expression, BindingRestrictions.Empty, outerObject)
{
FieldInfo innerField = outerObject.GetType().GetField(innerFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
var innerObject = innerField.GetValue(outerObject);
var innerDynamicProvider = innerObject as IDynamicMetaObjectProvider;
innerMetaObject = innerDynamicProvider.GetMetaObject(Expression.Field(Expression.Convert(Expression, LimitType), innerField));
}
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
return binder.FallbackInvokeMember(this, args, innerMetaObject.BindInvokeMember(binder, args));
}
}
}

Resources