Hi Jochen,
your first suggestion might do the trick and if closures disappear then
there will be much more that doesn't work.
We currently do something like this:
public void replaceImplicitThis(Expression invocation) {
if (invocation instanceof MethodCallExpression) {
MethodCallExpression methodCallExpression =
(MethodCallExpression)invocation;
if (methodCallExpression.isImplicitThis()) {
Expression target = referenceToCurrentClosure();
methodCallExpression.setObjectExpression(target);
}
}
}
private MethodCallExpression referenceToCurrentClosure() {
return new MethodCallExpression(
new VariableExpression("this"),
new ConstantExpression("each"),
new ArgumentListExpression(
new PropertyExpression(
new
ClassExpression(ClassHelper.makeWithoutCaching(Closure.class)),
new ConstantExpression("IDENTITY")
)
)
);
}
This looks a similar to your second suggestion.
Could you give me a hint on how to write a BytecodeExpersion for "ALOAD
0"? Could I just use this in place of the other MethodCallExpression
from referenceToCurrentClosure?
-Leo
Am 21.11.2017 um 19:32 schrieb Jochen Theodorou:
I am not saying I am getting the question fully...
but what you want is
def c = { return XY }
assert c() == c
something like that? Extend BytecodeExpersion and do a "ALOAD 0". No
guarantees that this will work forever though. In the future there
might be no closure class anymore.
Oh and if I got you wrong, and you want a reference to the enclosing
context, that would be the owner:
def x = this
def c = { return {owner}}
assert c()() == c
assert c.owner == x
bye Jochen
On 20.11.2017 21:27, Leonard Brünings wrote:
Hi,
I'm Leonard from the Spock framework team. Guillaume suggested that I
write to the dev-list with this problem.
Some context:
Spock has a method `with(Object, Closure)` in its Specification class
that sets the object as the delegate of the closure and transforms,
every call inside the closure to an implicit assertion.
given:
def person = new Person(name: "Peter", age: 28)
expect:
with(person) {
name == 'Peter'
age == 28
}
This worked fine for properties, however for single methods like
`contains` it didn't work.
The initial problem is described here
https://github.com/spockframework/spock/pull/606
Here is the gist:
This snippet
d|ef list = [1, 2] with(list) { contains(1) } |
transforms in AST (simplified) to
|def list = [1, 2] with(list) {
SpockRuntime.verifyMethodCondition(this, "contains", [1]) } |
then when the AST is written to bytecode it gets transformed again
|def list = [1, 2] with(list) {
SpockRuntime.verifyMethodCondition(this.getThisObject(), "contains",
[1]) }|
The problem is that the `contains` is now invoked on the containing
`Specification` instead of the `List`.
With the aforementioned PR it was changed to this
|def list = [1, 2] with(list) {
SpockRuntime.verifyMethodCondition(this.each(groovy.lang.Closure.IDENTITY),
"contains", [1]) } |
This "fix" now broke the nesting of `with` blocks as described here:
https://github.com/spockframework/spock/pull/782
Do you have any ideas on how to fix this elegantly?
- Cheers
Leonard
||||||
||