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

||||||

||



Reply via email to