Groovy 2.4 variable scope in closure with #Field annotation - groovy

Can someone explain to me why in closure2 initVars('c') is not able to modify the referenced object if #Field is used in declaration?
import groovy.transform.Field;
#Field def lines4 = "a";
void initVars(String pref){
println('init:'+lines4+' '+pref) //*3.init:a b *7.init:b c
lines4 = pref;
}
println("closure1") ///1. closure1
1.times {
println(lines4) ///2. a
initVars('b') ///3. init:a b
lines4 += 'p1'
println(lines4) ///4. bp1
}
println("closure2") ///5. closure2
1.times {
println(lines4) ///6. bp1
initVars('c') ///7. init:b c
println(lines4) ///8. bp1 Why not c
lines4 += 'q1'
println(lines4) ///9. bp1q1 Why not cq1
}
Output:
C:\projects\ATT>groovy test.groovy
1. closure1
2. a
3. init:a b
4. bp1
5. closure2
6. bp1
7. init:b c
8. bp1
9. bp1q1
Output without #Field and def, with just lines4 = "a" in script scope. This appears normal to me.
C:\projects\ATT>groovy test.groovy
1. closure1
2. a
3. init:a
4. bp1
5. closure2
6. bp1
7. init:bp1
8. c
9. cq1
I saw same behavior in groovy2.5-beta and groovy 2.6-alpha.

Using #Field annotation on a script variable changes a scope of this variable from a local one to a Script class one:
Variable annotation used for changing the scope of a variable within a script from being within the run method of the script to being at the class level for the script.
The annotated variable will become a private field of the script class. The type of the field will be the same as the type of the variable. Example usage:
import groovy.transform.Field
#Field List awe = [1, 2, 3]
def awesum() { awe.sum() }
assert awesum() == 6
In this example, without the annotation, variable awe would be a local script variable (technically speaking it will be a local variable within the run method of the script class). Such a local variable would not be visible inside the awesum method. With the annotation, awe becomes a private List field in the script class and is visible within the awesum method.
Source: http://docs.groovy-lang.org/2.4.12/html/gapi/groovy/transform/Field.html
Every Groovy script extends groovy.lang.Script class and the body of the script is executed inside Script.run() method. Groovy passes variables to this script using Binding object. When you change a scope of a local script variable to a class level then there is no binding for this variable passed to a closure, because binding object contains only local-scoped variables. Compare these two screenshots I made. First one shows what the binding object looks like when we call initVars(String pref) for the first time and lines4 is a local script variable:
And here is same breakpoint but now lines4 is a #Field def lines4 variable:
As you can see there is no binding for lines4 variable in binding object, but there is a class field called lines4, while this binding is present in the first screenshot attached.
When you call
lines4 += 'p1'
in the first closure, local binding for lines4 is created and it is initialized with a current value of a this.lines4 value. It happens because Script.getProperty(String property) is implemented in following way:
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
So it firstly checks if there is a binding for a variable you access in the closure and when it does not exist it passes execution to a parent's getProperty(name) implementation - in our case it just returns class property value. At this point this.lines4 is equal to b and this is the value that is returned.
initVars(String pref) method accesses class field, so when you call it it always overrides Script.lines4 property. But when you call
lines4 += 'q1'
in the second closure, the binding lines4 for a closure already exists and its value is bp1 - this value was associated in the first closure call. That's why you don't see c after calling initVars('c'). I hope it helps.
UPDATE: How binding works in a script explained
Let's get a little deeper to get better understanding what is going on under the hood. This is what your Groovy script looks like when it is compiled to a bytecode:
Compiled from "script_with_closures.groovy"
public class script_with_closures extends groovy.lang.Script {
java.lang.Object lines4;
public static transient boolean __$stMC;
public script_with_closures();
public script_with_closures(groovy.lang.Binding);
public static void main(java.lang.String...);
public java.lang.Object run();
public void initVars(java.lang.String);
protected groovy.lang.MetaClass $getStaticMetaClass();
}
Two things worth mentioning at this moment:
#Field def lines4 is compiled to a class field java.lang.Object lines4;
void initVars(String pref) method is compiled to public void initVars(java.lang.String); class method.
For a simplicity you can assume that the rest content (excluding lines4 and initVars method) of your script is inlined to public java.lang.Objectrun() method.
initVars always accesses class field lines4 because it has direct access to this field. Decompiling this method to a bytecode shows us this:
public void initVars(java.lang.String);
Code:
0: invokestatic #19 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
3: astore_2
4: aload_2
5: ldc #77 // int 5
7: aaload
8: aload_0
9: aload_2
10: ldc #78 // int 6
12: aaload
13: aload_2
14: ldc #79 // int 7
16: aaload
17: aload_2
18: ldc #80 // int 8
20: aaload
21: ldc #82 // String init:
23: aload_0
24: getfield #23 // Field lines4:Ljava/lang/Object;
27: invokeinterface #67, 3 // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.call:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
32: ldc #84 // String
34: invokeinterface #67, 3 // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.call:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
39: aload_1
40: invokeinterface #67, 3 // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.call:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
45: invokeinterface #52, 3 // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;Ljava/lang/Object;)Ljava/lang/Object;
50: pop
51: aload_1
52: astore_3
53: aload_3
54: aload_0
55: swap
56: putfield #23 // Field lines4:Ljava/lang/Object;
59: aload_3
60: pop
61: return
Operation 56 is a opcode for a assigning value to a field.
Now let's understand what happens when both closures gets called. First thing worth mentioning - both closures have delegate field set to the script object that is being executed. We know that it extends groovy.lang.Script class - a class that uses binding private field to store all bindings (variables) available in the script runtime. This is important observation, because groovy.lang.Script class overrides:
public Object getProperty(String property)
public void setProperty(String property, Object newValue)
Both methods use binding to lookup and store variables used in the script runtime. getProperty gets called any time you read local script variable and setProperty gets called any time you assign a value to script local variable. That's why code like:
lines4 += 'p1'
generates sequence like:
getProperty -> value + 'p1' -> setProperty
In your example first attempt of reading lines4 ends up with returning a value from parent class (it happens if binding is not found, then GroovyObjectSupport.getProperty(name) is called and this one returns a value of a class property with given name). When closure assigns a value to a lines4 variable then a binding is created. And because both closures share same binding object (they use delegate to the same instance), when second closure reads or writes line4 variable then it uses previously created binding. And initVars does not modify binding because as I shown you earlier it accesses class field directly.

Related

Groovy variable cannot be accessed within the function

/* Hello World in Groovy */
println("Hello world")
int a = 5
if (1 == 1){
println a
fcn() //line 11
}
def fcn(){
println a //line 15
}
This is my Groovy script, which gets the error
Hello world
5
Caught: groovy.lang.MissingPropertyException: No such property: a for class: main
groovy.lang.MissingPropertyException: No such property: a for class: main
at main.fcn(main.groovy:15)
at main.run(main.groovy:11)
when executed. Why the variable a is not available within the function fcn?
You can define variable a differently:
Option 1
a = 5
Option 2
import groovy.transform.Field
...
#Field int a = 5
The rational is to define a field in a scope of the script as opposed to the variable defined in the "run method" of the Script and hence not accessible from within other functions.
Consider checking this thread for more information
A link to Field annotation document also provides relevant information
Option 3
You can define the fcn as a proper function, which is in Groovy represented by Closure.
Then you can access the variables of outer scope:
int a = 5
def fcn = {
println a
}
if(true){
fcn()
}

How can I test that a field is set to a certain value?

This seems basic so I'm expecting this to be a dupe... but I haven't found anything that answers this question.
My app code is also Groovy. Say I have a field
def something
and in my test (where the CUT is a Spock Spy) I run a method in the middle of which there is a line
something = null
or
something = new Bubble()
... I'm simply trying to find a way of testing that something has indeed been set to null (or any value...)
In my then block I've tried:
1 * spyCUT.setSomething( null )
and
1 * spyCUT.setSomething(_)
and
1 * spyCUT.set( 'something', _ )
Incidentally, in answer to the objection that I could just test the value of something in the then block, the situation is that something is meant to be set first to one value and then to another in the course of this method...
Having read Groovy In Action 2nd Ed I have the vaguest of notions about how Groovy goes about dealing with getting and setting fields... Not enough, clearly.
MCVE (FWIW!)
class Spocko {
def something
def doStuff() {
something = 'fruit'
}
}
class SpockoTest extends Specification {
def 'test it'(){
given:
Spocko spySpocko = Spy( Spocko )
when:
spySpocko.doStuff()
then:
1 * spySpocko.setSomething(_)
}
}
LATER (after kriegaex's very helpful reply)
With above SpockTest where setSomething is invoked:
class Spocko {
def something
def doStuff() {
this.each{
it.something = 'fruit'
}
}
}
... passes! I'm trying now to understand why...
Incidentally I also find that the following passes (and doesn't without the closure):
1 * spySpocko.setProperty( 'something', _ )
After I have seen your MCVE, the question can be answered as follows: You cannot test for a method call which never happens. doStuff() just assigns a value to a field, it does not call a setter method internally. Look at this:
package de.scrum_master.stackoverflow
import spock.lang.Specification
class SpockoTest extends Specification {
static class Spocko {
def something
def doStuff() {
something = 'fruit'
}
def doMoreStuff() {
setSomething('vegetable')
}
}
def 'test it'(){
given: 'Spocko spy'
Spocko spySpocko = Spy(Spocko)
when: 'calling method assigning value to property'
spySpocko.doStuff()
then: 'no setter is called'
0 * spySpocko.setSomething(_)
spySpocko.something == 'fruit'
when: 'calling method using setter'
spySpocko.doMoreStuff()
then: 'setter gets called'
1 * spySpocko.setSomething('vegetable')
when: 'using Groovy setter-like syntax from another class'
spySpocko.something = 'fish'
then: 'actually a setter gets called'
1 * spySpocko.setSomething('fish')
}
}
This is what happens. When calling
javap -v target/test-classes/de/scrum_master/stackoverflow/SpockoTest\$Spocko.class
you see (output shortened):
public java.lang.Object doStuff();
descriptor: ()Ljava/lang/Object;
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: invokestatic #24 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
3: astore_1
4: ldc #36 // String fruit
6: astore_2
7: aload_2
8: aload_0
9: swap
10: putfield #38 // Field something:Ljava/lang/Object;
13: aload_2
14: areturn
15: aconst_null
16: areturn
public java.lang.Object doMoreStuff();
descriptor: ()Ljava/lang/Object;
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=1
0: invokestatic #24 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
3: astore_1
4: aload_1
5: ldc #40 // int 0
7: aaload
8: aload_0
9: ldc #42 // String vegetable
11: invokeinterface #48, 3 // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;Ljava/lang/Object;)Ljava/lang/Object;
16: areturn
17: aconst_null
18: areturn
Can you spot the difference?
Update after question edit 2: You wanted to know why this triggers the setter call:
def doStuff() {
this.each {
it.something = 'fruit'
}
}
This is because this is provided to the closure as a parameter, thus it.something = 'fruit' gets resolved dynamically just like in my example spySpocko.something = 'fish' because it is not an internal assignment like in something = 'fruit' (equivalent to this.something = 'fruit') anymore.
Actually I think this is not so difficult to understand even without looking at bytecode, just following the usual Groovy tutorials. I am repeating myself, but I do think you are over-engineering and over-complicating things a bit, testing things too deeply. I would not put tests like these into a production code base. Try to test the behaviour of your classes (think specifications and features!), not the innards' intricacies. But if it helps you understand how Groovy works, just continue playing.
As of now, please refrain from further question edits and follow-up questions. If you have a new problem, it would be better to create a new question with a new MCVE.

Strings in java 8 less memory

I got below code and I was asked which option gets the following pattern:
XXXX-XXXX-XXXX-2324
...
Code Below:
public class CCMark {
public static String maskCC(String creditCard){
String x = "XXXX-XXXX-XXXX-";
//line 1
}
public static void main(String[] args) {
System.out.println(maskCC("1234-5678-1234-2324"));
System.out.println(maskCC("4567-5678-1234-5643"));
System.out.println(maskCC("1234-5678-1234-4654"));
System.out.println(maskCC("4567-5678-1234-5435"));
}
}
Below possible options that can be inserted on "line 1":
A)
return x + creditCard.substring(15, 19);
B)
StringBuilder sb = new StringBuilder(x);
sb.append(creditCard, 15, 19);
return sb.toString();
I think that the best option here, as A and B provide us with the same output, is B, because it is using StringBuilder which means that its approach is mutable, so it will use less memory than option A.
Am I wrong? Could it be that option A for this particular situation is the best option?
Options a and b are identical, because the Java compiler will convert option a into option b. You could move the declaration of x outside the method (and make it final). Something like,
static final String x = "XXXX-XXXX-XXXX-";
public static String maskCC(final String creditCard) {
return x + creditCard.substring(15, 19);
}
Using javap to check the first against, the second. Java code like,
String x = "XXXX-XXXX-XXXX-";
String creditCard = "1234-5678-1234-23324";
String x2 = x + creditCard.substring(15, 19);
StringBuilder sb = new StringBuilder(x);
sb.append(creditCard, 15, 19);
String x3 = sb.toString();
generates byte-code that looks like (note lines 6-31 and 32-58)
0: ldc #16 // String XXXX-XXXX-XXXX-
2: astore_1
3: ldc #18 // String 1234-5678-1234-23324
5: astore_2
6: new #20 // class java/lang/StringBuilder
9: dup
10: aload_1
11: invokestatic #22 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
14: invokespecial #28 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
17: aload_2
18: bipush 15
20: bipush 19
22: invokevirtual #31 // Method java/lang/String.substring:(II)Ljava/lang/String;
25: invokevirtual #35 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #39 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_3
32: new #20 // class java/lang/StringBuilder
35: dup
36: aload_1
37: invokespecial #28 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
40: astore 4
42: aload 4
44: aload_2
45: bipush 15
47: bipush 19
49: invokevirtual #43 // Method java/lang/StringBuilder.append:(Ljava/lang/CharSequence;II)Ljava/lang/StringBuilder;
52: pop
53: aload 4
55: invokevirtual #39 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
58: astore 5
60: return
The big advantage of the variant A, return x + creditCard.substring(15, 19); is that it is simple and clean and it works in all Java versions from 1 to 8. In the case that its compiled form uses StringBuffer, a simple recompile for Java 5 or newer will make it use StringBuilder instead. This flexibility is lost when you work with either, StringBuffer or StringBuilder, manually.
The exact compiled form is not fixed. Since the semantic of the method String.substring is not fixed by the Java Language Specification, compilers usually won’t touch this and compile it as an ordinary method invocation. The specification encourages compiler vendors to use StringBuilder for string concatenation (the + operator) whenever there is a benefit and most compilers will do so, even when there is no benefit. Here, both, x and the result of substring, are Strings so a simple String.concat would be simpler but most compilers always use StringBuilder, compiling variant A to the equivalent of return new StringBuilder().append(x).append(creditCard.substring(15, 19)).toString();.
Comparing this typical form with your variant B, we can conclude that variant B has two advantages performance-wise:
new StringBuilder(x) initializes the StringBuilder to a capacity of x.length()+16 which is sufficient for the entire operation, whereas the default capacity of new StringBuilder(), typically used for variant A, is fixed to 16 characters which misses the mark here as we have a result of 19 characters, thus a reallocation and copying of the underlying character array will occur
sb.append(creditCard, 15, 19); will copy the four characters without the need to create an intermediate String representation of these characters. The expenses of the substring operation differ depending on the implementation, e.g. in Oracle’s implementation there was a significant change with version 1.7.0_06; starting with this version a substring requires a new char[] array holding a copy of the affected character data as it doesn’t maintain a separate offset and length field
But note that all these differences of variant A and B only affect the formal description of the operation to perform. What will actually happen, is up to the JVM/JRE and usually the Hotspot optimizer knows a lot of string related operations and may fuse operations or elide intermediate string representations. Thus, the outcome regarding performance is rather unpredictable and may be affected by subtle changes to the implementation.
That’s why developers might stick to variant A which is, as said, simpler and more readable, and only care for performance once a profiler tells them that there is a performance problem that could be solved by dealing with Stringbuilder manually.

Groovy - Type checking in script not working as expected

I have a Groovy application in which I allow the user to add custom behavior via Groovy scripts. I include those scripts via GroovyShell and type check them via Type Checking Extensions. The full code of how I include the script in my application is:
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
new ASTTransformationCustomizer(TypeChecked)
)
def shell = new GroovyShell(config)
shell.evaluate(new File("path/to/some/file.groovy"))
This works fine. However, type checking in the script seems to be seriously broken. For example, I can include the following scripts without any complaint from the compiler:
String test = getTestValue() // automatic conversion from Integer to String. But WHY?
println "The value is $test" // shows as "The value is 0" on the console
private Integer getTestValue(){
return 0
}
I can even go further than that. When creating a class inside the script, I can assign it to a String without any error:
String y = new Test()
println y // shows Test#somenr on the console
class Test { }
Other type checks do work. I have not discovered any logic behind it yet, so any pointers in the right direction are greatly appreciated.
If in doubt, disasm. This is the bit around a call similar to yours: String x = new T():
0: invokestatic #17 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
3: astore_1
4: aload_1
5: ldc #40 // int 1
7: aaload
8: ldc #42 // class T
10: invokeinterface #46, 2 // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callConstructor:(Ljava/lang/Object;)Ljava/lang/Object;
15: invokestatic #52 // Method org/codehaus/groovy/runtime/typehandling/ShortTypeHandling.castToString:(Ljava/lang/Object;)Ljava/lang/String;
18: checkcast #54 // class java/lang/String
So this is the culprit for that cast. This seems also to hold true for #TypeChecked/#CompileStatic.
This is most likely a bug in the Static Type Checker. When LHS of the expression is a String variable, a conversion invoking ShortTypeHandling.castToString() is applied to the RHS.
This holds true as of Groovy 2.4.13.

Groovy's #CompileStatic and map constructors

I'm using #CompileStatic for the first time, and confused as to how Groovy's map constructors work in this situation.
#CompileStatic
class SomeClass {
Long id
String name
public static void main(String[] args) {
Map map = new HashMap()
map.put("id", 123L)
map.put("name", "test file")
SomeClass someClass1 = new SomeClass(map) // Does not work
SomeClass someClass2 = map as SomeClass // Works
}
}
Given the code above I see the following error when trying to compile
Groovyc: Target constructor for constructor call expression hasn't been set
If #CompileStatic is removed, both constructors work properly.
Can anyone explain why new SomeClass(map) does not compile with #CompileStatic? And a possible addition, why does map as SomeClass still work?
Groovy actually does not give you a "Map-Constructor". The constructors
in your class are what you write down. If there are none (like in your case),
then there is the default c'tor.
But what happens, if you use the so called map c'tor (or rather call it
"object construction by map")? The general approach of groovy is like this:
create a new object using the default c'tor (this is the reason, why the
construction-by-map no longer works, if there would be just e.g.
SomeClass(Long id, String name))
then use the passed down map and apply all values to the properties.
If you disassmble your code (with #CompileDynamic (the default)) you see, that
the construction is handled by CallSite.callConstructor(Object,Object),
which boils down to this this code area.
Now bring in the version of this construction by map, that is more familiar
for the regular groovyist:
SomeClass someClass3 = new SomeClass(id: 42L, name: "Douglas").
With the dynamic version of the code, the disassembly of this looks actually
alot like your code with the map. Groovy creates a map from the param(s) and
sends it off to callConstructor - so this is actually the same code path
taken (minus the implicit map creation).
For now ignore the "cast-case", as it is actually the same for both static and
dynamic: it will be sent to ScriptBytecodeAdapter.asType which basically
gives you the dynamic behaviour in any case.
Now the #CompileStatic case: As you have witnessed, your call with an
explicit map for the c'tor no longer works. This is due to the fact, that
there never was an explicit "map-c'tor" in the first place. The class still
only has its default c'tor and with static compilation groovyc now can just
work with the things that are there (or not if there aren't in this case).
What about new SomeClass(id: 42L, name: "Douglas") then? This still works
with static compilation! The reason for this is, that groovyc unrolls this
for you. As you can see, this simply boils down to def o = new SomeClass();
o.setId(42); o.setName('Douglas'):
new #2 // class SomeClass
dup
invokespecial #53 // Method "<init>":()V
astore_2
ldc2_w #54 // long 42l
dup2
lstore_3
aload_2
lload_3
invokestatic #45 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
invokevirtual #59 // Method setId:(Ljava/lang/Long;)V
aconst_null
pop
pop2
ldc #61 // String Douglas
dup
astore 5
aload_2
aload 5
invokevirtual #65 // Method setName:(Ljava/lang/String;)V
As the CompileStatic documentation says:
will actually make sure that the methods which are inferred as being
called will effectively be called at runtime. This annotation turns
the Groovy compiler into a static compiler, where all method calls are
resolved at compile time and the generated bytecode makes sure that
this happens
As a result, a constructor with a Map argument is searched in the static compilation to "resolve it at compile time", but it is not found and thereby there is a compilation error:
Target constructor for constructor call expression hasn't been set
Adding such a constructor solves the issue with the #CompileStatic annotation, since it is resolved at compile time:
import groovy.transform.CompileStatic
#CompileStatic
class SomeClass {
Long id
String name
SomeClass(Map m) {
id = m.id as Long
name = m.name as String
}
public static void main(String[] args) {
Map map = new HashMap()
map.put("id", 123L)
map.put("name", "test file")
SomeClass someClass1 = new SomeClass(map) // Now it works also
SomeClass someClass2 = map as SomeClass // Works
}
}
You can check StaticCompilationVisitor if you want to dig deeper.
Regarding the line
SomeClass someClass2 = map as SomeClass
You are using there the asType() method of Groovy's GDK java.util.Map, so it is therefore solved at runtime even in static compilation:
Coerces this map to the given type, using the map's keys as the public
method names, and values as the implementation. Typically the value
would be a closure which behaves like the method implementation.

Resources