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
>
>

Reply via email to