Yes this is cool. I was wondering what if you try and modify the Meta-Link architecture itself, but my brain did not reach this second meta level...
2018-04-04 21:50 GMT+02:00 Sven Van Caekenberghe <s...@stfx.eu>: > Very cool. Impressive combination of unique Pharo features. > > > On 4 Apr 2018, at 18:02, 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 > > > > > > >