I am using groovy 2.4.12 with Oracle JVM 1.8. I am trying to understand a bit how groovyc converts the scripts written by end users.
To that end I wrote this simple script:
println 'Hello World`
This was compiled to bytecode using groovyc hello.groovy. Finally, I decompiled the hello.class to get the following code:
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class hello extends Script {
public hello() {
CallSite[] var1 = $getCallSiteArray();
}
public hello(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, hello.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
return var1[1].callCurrent(this, "Hello World");
}
}
This looks like a typical Java class except I cannot figure out where $getCallSiteArray() method is defined. It is definitely not in this class and neither is it a public or protected member of groovy.lang.Script. So my question is where is this method implemented?
use another decompiler to see it
//
// Decompiled by Procyon v0.5.36
//
public class A extends Script
{
private static /* synthetic */ SoftReference $callSiteArray;
public A() {
$getCallSiteArray();
}
public A(final Binding context) {
$getCallSiteArray();
super(context);
}
public static void main(final String... args) {
$getCallSiteArray()[0].call((Object)InvokerHelper.class, (Object)A.class, (Object)args);
}
public Object run() {
return $getCallSiteArray()[1].callCurrent((GroovyObject)this, (Object)"hello world");
}
private static /* synthetic */ CallSiteArray $createCallSiteArray() {
final String[] array = new String[2];
$createCallSiteArray_1(array);
return new CallSiteArray((Class)A.class, array);
}
private static /* synthetic */ CallSite[] $getCallSiteArray() {
CallSiteArray $createCallSiteArray;
if (A.$callSiteArray == null || ($createCallSiteArray = A.$callSiteArray.get()) == null) {
$createCallSiteArray = $createCallSiteArray();
A.$callSiteArray = new SoftReference($createCallSiteArray);
}
return $createCallSiteArray.array;
}
}
Related
I have the following code. I have an abstract JobParams, a class extending that abstract GradleJobParams, and a gjp variable with value using anonymous class declaration.
I want to test the overriding behavior of groovy. I can override the method setupRoot() but not the property testVar, why is that?
Tested on: https://groovyconsole.appspot.com/script/5146436232544256
abstract class JobParams {
int root
def testVar=1
def setupRoot () {
println("The root");
}
def printTestVar () {
println("The testVar:" + testVar);
}
}
class GradleJobParams extends JobParams {
}
def gjp = [
testVar:3,
setupRoot:{
println("Override root");
}
] as GradleJobParams;
println("Starting");
gjp.printTestVar();
gjp.setupRoot();
The result is:
Starting
The testVar:1
Override root
Java (and thus Groovy) does not support overriding fields from the parent class with subclassing. Instead, it uses a mechanism called hiding fields:
Hiding Fields
Within a class, a field that has the same name as a field in the superclass hides the superclass's field, even if their types are different. Within the subclass, the field in the superclass cannot be referenced by its simple name. Instead, the field must be accessed through super, which is covered in the next section. Generally speaking, we don't recommend hiding fields as it makes code difficult to read.
Source: https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html
It can be simply illustrated with the following example in Java:
final class SubclassHiddingFieldExample {
static abstract class A {
int value = 10;
void printValue1() {
System.out.println(value);
}
void printValue2() {
System.out.println(this.value);
}
void printValue3() {
System.out.println(((B)this).value);
}
}
static class B extends A {
int value = 12;
}
public static void main(String[] args) {
final B b = new B();
b.printValue1();
b.printValue2();
b.printValue3();
}
}
Output:
10
10
12
As you can see, only printValue3 prints out 3, because it cast this explicitly to B class.
Now, if you look at the decompiled bytecode of your JobParams class, you can see that the printTestVar method code is an equivalent of the following Java code:
//
// 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.callsite.CallSite;
public abstract class JobParams implements GroovyObject {
private int root;
private Object testVar;
public JobParams() {
CallSite[] var1 = $getCallSiteArray();
byte var2 = 1;
this.testVar = Integer.valueOf(var2);
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
}
public Object setupRoot() {
CallSite[] var1 = $getCallSiteArray();
return var1[0].callCurrent(this, "The root");
}
public Object printTestVar() {
CallSite[] var1 = $getCallSiteArray();
return var1[1].callCurrent(this, var1[2].call("The testVar:", this.testVar));
}
public MetaClass getMetaClass() {
MetaClass var10000 = this.metaClass;
if (var10000 != null) {
return var10000;
} else {
this.metaClass = this.$getStaticMetaClass();
return this.metaClass;
}
}
public void setMetaClass(MetaClass var1) {
this.metaClass = var1;
}
public Object invokeMethod(String var1, Object var2) {
return this.getMetaClass().invokeMethod(this, var1, var2);
}
public Object getProperty(String var1) {
return this.getMetaClass().getProperty(this, var1);
}
public void setProperty(String var1, Object var2) {
this.getMetaClass().setProperty(this, var1, var2);
}
public int getRoot() {
return this.root;
}
public void setRoot(int var1) {
this.root = var1;
}
public Object getTestVar() {
return this.testVar;
}
public void setTestVar(Object var1) {
this.testVar = var1;
}
}
You can see that the line that prints out the value of the testVar field is represented by:
return var1[1].callCurrent(this, var1[2].call("The testVar:", this.testVar));
It means that no matter what value of testVar your subclass defines, the printTestVar method uses testVar field defined in the JobParams class. Period.
Using Groovy auto getter methods
There is one way you to implement the expected behavior. Every class field in Groovy has a getter method associated with that field compiled by Groovy for you. It means that you can access testVar by calling the getTestVar() method generated by the Groovy compiler. You can use it to override the value returned by a getter method for any field from the subclass. Consider the following example:
abstract class JobParams {
int root
def testVar=1
def setupRoot () {
println("The root");
}
def printTestVar () {
println("The testVar:" + getTestVar()); // <-- using a getTestVar() method instead a testVar field
}
}
class GradleJobParams extends JobParams {
}
def gjp = [
getTestVar: 3, // <-- stubbing getTestVar() method to return a different value
setupRoot:{
println("Override root");
}
] as GradleJobParams;
println("Starting");
gjp.printTestVar();
gjp.setupRoot();
Output:
Starting
The testVar:3
Override root
It took me way too long to set up a listener on one property of the objects in my Observablelist and add a listener to it.
ObservableList<Track> observableResult = FXCollections.observableArrayList((Track tr)-> new Observable[]{tr.selectedProperty()});
observableResult.addListener(new ListChangeListener<Track>() {
#Override
public void onChanged(Change<? extends Track> c) {
c.next();
for(Track k : c.getAddedSubList()){
System.out.println(k.getTrackName());
}
}
});
But I can't seem to be able to locate the actual object the change has been made to. The Change class only appears to support added and removed members, which don't get triggered by the actual changes inside them.
I have a workaround for this, just calling another method that would loop trough the entire ObservableArrayList and get for example, only the selected items, but that gets pretty expensive after I have a couple of thousand objects. Finding the source members that got changed would allow me to just push them to another array and save a bunch of overhead.
You can call getFrom() on the change to get the index of the changed item. I don't think there's a way to actually figure out which property changed (if you have more than one property listed in the extractor) or get the old value, but maybe this is enough.
If you need more, you could consider registering your own listeners with the list to track, which would be tricky but not impossible.
Here's an SSCCE demonstrating the getFrom() call:
import java.util.Random;
import java.util.stream.IntStream;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
public class ListChangeListenerTest {
public static void main(String[] args) {
ObservableList<Item> itemList = FXCollections.observableArrayList(item -> new Observable[]{item.valueProperty()});
itemList.addListener((Change<? extends Item> c) -> {
while (c.next()) {
if (c.wasUpdated()) {
int index = c.getFrom();
System.out.println("Updated item at "+index+" new value is "+itemList.get(index).getValue());
}
}
});
IntStream.rangeClosed(1, 1000).mapToObj(Item::new).forEach(itemList::add);
Random rng = new Random();
itemList.get(rng.nextInt(itemList.size())).setValue(rng.nextInt(10000));
}
public static class Item {
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(int value) {
setValue(value);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
}
}
Here's a version that manages the listeners on the property manually. Note that
This doesn't use an extractor on the list
The property in the Item bean is constructed passing a reference to the bean that owns the property. This allows the listener on the property to get a reference to the Item (via a bit of ugly downcasting)
This gives a bit more flexibility; e.g. if you wanted to check modifications on multiple properties and perform different actions, this would allow this. As you can see, the listener can also access the old value.
import java.util.Random;
import java.util.stream.IntStream;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
public class ListChangeListenerTest {
public static void main(String[] args) {
ChangeListener<Number> valueListener = (obs, oldValue, newValue) -> {
Item item = (Item) ((Property<?>) obs).getBean();
System.out.println("Value for "+item+" changed from " + oldValue + " to "+newValue);
};
ObservableList<Item> itemList = FXCollections.observableArrayList();
itemList.addListener((Change<? extends Item> change) -> {
while (change.next()) {
if (change.wasAdded()) {
for (Item item : change.getAddedSubList()) {
item.valueProperty().addListener(valueListener);
}
}
if (change.wasRemoved()) {
for (Item item : change.getRemoved()) {
item.valueProperty().removeListener(valueListener);
}
}
}
});
IntStream.rangeClosed(1, 1000).mapToObj(Item::new).forEach(itemList::add);
Random rng = new Random();
itemList.get(rng.nextInt(itemList.size())).setValue(rng.nextInt(10000));
}
public static class Item {
private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
private final String id ;
public Item(int value) {
id = "Item "+value ;
setValue(value);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
#Override
public String toString() {
return id ;
}
}
}
Finally, if you want to account for "bulk" updates, you need to implement ObservableList yourself. You can do this by subclassing ModifiableObservableListBase, and the basic idea is pretty straightforward. The implementation is made a bit tedious by having to create the Change object representing the update, but it's not too bad. Here's an example that allows updating a contiguous range:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ModifiableObservableListBase;
public class UpdatingObservableList<T> extends ModifiableObservableListBase<T> {
private final List<T> list ;
public UpdatingObservableList(List<T> list) {
this.list = list ;
}
public UpdatingObservableList() {
this(new ArrayList<>());
}
public void updateSublist(int start, int end, Consumer<T> updater) {
if (start < 0) throw new ArrayIndexOutOfBoundsException("Start ("+start+") cannot be < 0");
if (end < start) throw new IllegalArgumentException("End ("+end+") cannot be less than start ("+start+")");
if (end > size()) throw new ArrayIndexOutOfBoundsException("End ("+end+") cannot be greater than list size ("+size()+")");
for (T element : list.subList(start, end)) {
updater.accept(element);
}
fireChange(createUpdate(start, end));
}
#Override
public T get(int index) {
return list.get(index);
}
#Override
public int size() {
return list.size();
}
#Override
protected void doAdd(int index, T element) {
list.add(index, element);
}
#Override
protected T doSet(int index, T element) {
return list.set(index, element);
}
#Override
protected T doRemove(int index) {
return list.remove(index);
}
private Change<T> createUpdate(int start, int end) {
return new Change<T>(this) {
private boolean initialState = true ;
#Override
public boolean next() {
if (initialState) {
initialState = false ;
return true ;
}
return false ;
}
#Override
public void reset() {
initialState = true ;
}
#Override
public int getFrom() {
checkState();
return start ;
}
#Override
public int getTo() {
checkState();
return end ;
}
#Override
public List<T> getAddedSubList() {
checkState();
return Collections.emptyList();
}
#Override
public List<T> getRemoved() {
checkState();
return Collections.emptyList();
}
#Override
protected int[] getPermutation() {
checkState();
return new int[0];
}
#Override
public boolean wasAdded() {
checkState();
return false ;
}
#Override
public boolean wasRemoved() {
checkState();
return false ;
}
#Override
public boolean wasUpdated() {
return true ;
}
#Override
public boolean wasPermutated() {
checkState();
return false ;
}
#Override
public int getRemovedSize() {
checkState();
return 0 ;
}
#Override
public int getAddedSize() {
checkState();
return 0 ;
}
private void checkState() {
if (initialState) {
throw new IllegalStateException("Must call Change.next()");
}
}
};
}
}
and here's a version of the test class that uses this. Note that the update is performed via the list:
import java.util.Random;
import java.util.stream.IntStream;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ListChangeListener.Change;
public class ListChangeListenerTest {
public static void main(String[] args) {
UpdatingObservableList<Item> itemList = new UpdatingObservableList<Item>();
itemList.addListener((Change<? extends Item> change) -> {
while (change.next()) {
if (change.wasUpdated()) {
for (int i = change.getFrom() ; i < change.getTo() ; i++) {
System.out.println(itemList.get(i) + " updated - new value: "+itemList.get(i).getValue());
}
}
}
});
IntStream.rangeClosed(1, 1000).mapToObj(Item::new).forEach(itemList::add);
Random rng = new Random();
int start = rng.nextInt(itemList.size());
int end = Math.min(itemList.size(), start + 1 + rng.nextInt(15));
itemList.updateSublist(start, end, item -> item.setValue(rng.nextInt(10000)));
}
public static class Item {
private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
private final String id ;
public Item(int value) {
id = "Item "+value ;
setValue(value);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
#Override
public String toString() {
return id ;
}
}
}
Is threre any way to ues override method in anonymous class on Groovy #CompileStatic annotation?
groogy source
import groovy.transform.CompileStatic;
interface HelloWorld {
public void greet();
}
class HelloWorldAnonymousClassesParents {
public void hi() {
println "hi"
}
}
#CompileStatic
public class HelloWorldAnonymousClasses extends HelloWorldAnonymousClassesParents {
public void hi() {
System.out.println("hihi ");
}
public void sayHello() {
HelloWorld spanishGreeting = new HelloWorld() {
public void greet() {
hi() //<- here [Static type checking] - Reference to method is ambiguous error
System.out.println("spanishGreeting");
}
};
spanishGreeting.greet();
hi()
}
}
def myApp = new HelloWorldAnonymousClasses();
myApp.sayHello();
Same source in java run well
java source
package org.octopus;
class HelloWorldAnonymousClassesParents {
public void hi() {
System.out.println("hi ");
}
}
interface HelloWorld {
public void greet();
}
public class Test extends HelloWorldAnonymousClassesParents{
public void hi() {
System.out.println("hihi ");
}
public void sayHello() {
HelloWorld spanishGreeting = new HelloWorld() {
public void greet() {
hi();
System.out.println("spanishGreeting");
}
};
spanishGreeting.greet();
hi();
}
public static void main(String... args) {
Test myApp = new Test();
myApp.sayHello();
}
}
How can I avoid that error with #CompileStatic annotation?
You can write it as a closure. This implies an as HelloWorld and as the interface only has one method groovy can deduct this.
HelloWorld spanishGreeting = {
hi()
System.out.println("spanishGreeting");
}
above code is groovy 2.3; with earlier groovy 2 versions it needs an explicit cast like
def spanishGreeting = {/*...*/} as HelloWorld
Im trying to write simple things with Apache Wicket (6.15.0) and Groovy (2.2.2 or 2.3.1). And Im having trouble with inner classes.
class CreatePaymentPanel extends Panel {
public CreatePaymentPanel(String id) {
super(id)
add(new PaymentSelectFragment('currentPanel').setOutputMarkupId(true))
}
public class PaymentSelectFragment extends Fragment {
public PaymentSelectFragment(String id) {
super(id, 'selectFragment', CreatePaymentPanel.this) // problem here
add(new AjaxLink('cardButton') {
#Override
void onClick(AjaxRequestTarget target) {
... CreatePaymentPanel.this // not accessible here
}
})
add(new AjaxLink('terminalButton') {
#Override
void onClick(AjaxRequestTarget target) {
... CreatePaymentPanel.this // not accessible here
}
});
}
} // end of PaymentSelectFragment class
} // end of CreatePaymentPanel class
Groovy tries to find a property "this" in CreatePaymentPanel class.. How to workaround this? It is a valid java code, but not groovy.
However,
Test.groovy:
class Test {
static void main(String[] args) {
def a = new A()
}
static class A {
A() {
def c = new C()
}
public void sayA() { println 'saying A' }
class B {
public B(A instance) {
A.this.sayA()
instance.sayA()
}
}
/**
* The problem occurs here
*/
class C extends B {
public C() {
super(A.this) // groovy tries to find property "this" in A class
sayA()
}
}
}
}
Above code wont work, the same error occurs, like in Wicket's case.
And TestJava.java, the same and working:
public class TestJava {
public static void main(String[] args) {
A a = new A();
}
static class A {
A() {
C c = new C();
}
public void sayA() {
System.out.println("saying A");
}
class B {
public B(A instance) {
instance.sayA();
}
}
/**
* This works fine
*/
class C extends B {
public C() {
super(A.this);
sayA();
}
}
}
}
What I am missing?
You can't refer to a CreatePaymentPanel.this inside of PaymentSelectFragment because there is no instance of CreatePamentPanel that would be accessible there. What would you expect that to evaluate to if it were allowed?
I'm very new to Groovy. I wonder how Closures are implemented in Groovy.
Lets say :
def a = { println "Hello" }
a()
when a() is done, what is actually happening behind the scenes? Which method does a() calls to make the closure executable?
Thanks in advance.
Basically:
your closure is a class with specific name
a() invokes doCall() which invokes doCall(Object it) (implicit it in closures)
acallsite contains method names (2 x println) - and are invoked with appropriate arguments
Here you go:
For this Groovy Script:
def a = { println "Hello"; println "Hello2" }
a()
Closure a looks like this:
class Test$_run_closure1 extends Closure
implements GeneratedClosure
{
public Object doCall(Object it)
{
CallSite acallsite[] = $getCallSiteArray();
acallsite[0].callCurrent(this, "Hello");
return acallsite[1].callCurrent(this, "Hello2");
}
public Object doCall()
{
CallSite acallsite[] = $getCallSiteArray();
return doCall(null);
}
protected MetaClass $getStaticMetaClass()
{
if(getClass() != Test$_run_closure1)
return ScriptBytecodeAdapter.initMetaClass(this);
ClassInfo classinfo = $staticClassInfo;
if(classinfo == null)
$staticClassInfo = classinfo = ClassInfo.getClassInfo(getClass());
return classinfo.getMetaClass();
}
public static void __$swapInit()
{
CallSite acallsite[] = $getCallSiteArray();
$callSiteArray = null;
}
private static void $createCallSiteArray_1(String as[])
{
as[0] = "println";
as[1] = "println";
}
private static CallSiteArray $createCallSiteArray()
{
String as[] = new String[2];
$createCallSiteArray_1(as);
return new CallSiteArray(Test$_run_closure1, as);
}
private static CallSite[] $getCallSiteArray()
{
CallSiteArray callsitearray;
if($callSiteArray == null || (callsitearray = (CallSiteArray)$callSiteArray.get()) == null)
{
callsitearray = $createCallSiteArray();
$callSiteArray = new SoftReference(callsitearray);
}
return callsitearray.array;
}
static Class _mthclass$(String s)
{
try
{
return Class.forName(s);
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
private static ClassInfo $staticClassInfo;
public static transient boolean __$stMC;
private static SoftReference $callSiteArray;
static
{
__$swapInit();
}
public Test$_run_closure1(Object _outerInstance, Object _thisObject)
{
CallSite acallsite[] = $getCallSiteArray();
super(_outerInstance, _thisObject);
}
}
It ends up calling one of the Closure.call methods (in this case the one with no args)
There's more info about this in the documentation