you should use that as an example in the reflectivity paper.
On Wed, Apr 4, 2018 at 6:02 PM, Marcus Denker <marcus.den...@inria.fr> wrote:
> Hi,
>
> Some code is very “active”, executed all the time, e.g. collections,
> graphics… or imagine if you work on the compiler or debugger.
>
> It would really be nice if we could test a change before accepting it.
> Something like “Accept for Test” where magically the original method
> does not change, yet, when running tests, the version of the code we accepted
> for testing is executed.
>
> So how do we implement that?
>
> 1) menu in the browser. I did a quick hack, a Suggestions AST menu shows for
> all nodes (to be replaced with the “correct” way later).
>
> SugsSuggestion subclass: #SugsAcceptForTest
> instanceVariableNames: ''
> classVariableNames: ''
> package: 'SmartSuggestions-Suggestion’
>
> label
> ^'Accept for test’
>
> So, now we can do everything in the #execute method.
>
> What do we need?
>
> 1) How we know that we are in a test? —> CurrentExecutionEnvironment value
> isTest
> 2) can we compile the buffer even if there are syntax errors (kind of what I
> expect to happen in non-accepted code)?
> —> yes, remember, we use the same Parser for syntax highlighting and
> we extended code-generation for SyntaxError Nodes
> in the past (it raises a syntax error at runtime, isn’t that
> kind off odd in a fun way?).
>
> newMethod := context selectedClass compiler
> source: context code;
> options: #(+ optionParseErrors);
> compile.
>
> 3) Now how to we “hook into” the execution of the original unchanged method?
> We use a MetaLink. We install it as a before link on
> the RBMethodNode, the meta-object is a block. This block gets as
> arguments the thisContext and the arguments of the original method
> and then just does what you want it do do: if we are in test, return the
> result of the execution of the method we just compiled.
> (Closures are fun, we can just reference the outer temp newMethod, no need
> to store it anywhere else explicitly):
>
> [ :aContext :args |
> CurrentExecutionEnvironment value isTest
> ifTrue: [ aContext return: (newMethod
> valueWithReceiver: aContext receiver arguments: args) ] ];
>
> Thus, all the code is this:
>
> execute
> | newMethod metaLink |
> “we compile a new method from the not-accepted text editor. Allow
> syntax errors"
> newMethod := context selectedClass compiler
> source: context code;
> options: #(+ optionParseErrors);
> compile.
> "the link executes the method we just created and returns"
> metaLink := MetaLink new
> metaObject: [ :aContext :args |
> CurrentExecutionEnvironment value isTest
> ifTrue: [ aContext return: (newMethod
> valueWithReceiver: aContext receiver arguments: args) ] ];
> selector: #value:value:;
> arguments: #(context arguments).
> context selectedMethod ast link: metaLink
>
> Cleanup we do not care as everything will be GCed as soon as we accept the
> method for real.
>
> And it works! I did not test it too much, but it seems to do what it is
> supposed to do. (but more tests and testing are for sure needed)
> https://github.com/pharo-project/pharo/pull/1182
>
> Next step: move it to a better place in the menu.
>
> Marcus
>
>