<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
pre
        {mso-style-priority:99;
        mso-style-link:"HTML Preformatted Char";
        margin:0in;
        margin-bottom:.0001pt;
        font-size:10.0pt;
        font-family:"Courier New";
        color:black;}
span.HTMLPreformattedChar
        {mso-style-name:"HTML Preformatted Char";
        mso-style-priority:99;
        mso-style-link:"HTML Preformatted";
        font-family:"Courier New";
        color:black;}
.MsoChpDefault
        {mso-style-type:export-only;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
--></style>
</head>
<body lang="EN-US" link="blue" vlink="#954F72" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal">Hi Juan,</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I'm enclosing an updated changeset (please disregard the previous one); I made a stupid assumption about #runUntilErrorOrReturnFrom:. The new changeset leaves it untouched and adds its replica called #runUnwindUntilErrorOrReturnFrom: with
 the required functionality.</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I'd like to point out a little issue with the proposed semantics of nested errors during termination:
</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Below’s a simple example. If you abandon the first debugger it means you want to terminate the execution and unwind the terminated process but an error happens during the unwind. You get another debugger reporting the error and asking you
 what to do next - to debug the error or proceed. But proceeding a termination really means to proceed with the *<b>unwind business only</b>* and not to proceed with the original computation. Here's my point: Hitting Proceed will resume the original computation
 beyond the unwind blocks but Abandon will only finish the unwind procedure which is exactly what we want. In Squeak I simply disabled the Proceed button but in Cuis I couldn't see such a simple solution so I left it unresolved.</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">[self error: 'x1'] ensure: [</p>
<p class="MsoNormal">    [self error: 'x2'] ensure: [</p>
<p class="MsoNormal">        Transcript show: '2'].</p>
<p class="MsoNormal">    Transcript show: '1'].</p>
<p class="MsoNormal">Transcript show: '0'</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">---> you want to see '21' but proceeding the debugger will give you '210' where '0' is printed outside of unwind blocks.</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">So just that you know there's still a little inconsistency in the termination semantics.</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">One more question: do you have an idea why BlockCannotReturn error is considered resumable? I'd say it's unresumable by definition…? Thanks :)</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">best regards,</p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Jaromir</p>
<p class="MsoNormal"><o:p> </o:p></p>
<div style="mso-element:para-border-div;border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal" style="border:none;padding:0in"><b>From: </b><a href="mailto:cuis-dev@lists.cuis.st">Jaromir Matas via Cuis-dev</a><br>
<b>Sent: </b>Wednesday, May 12, 2021 17:02<br>
<b>To: </b><a href="mailto:cuis-dev@lists.cuis.st">Discussion of Cuis Smalltalk</a><br>
<b>Cc: </b><a href="mailto:mail@jaromir.net">Jaromir Matas</a>; <a href="mailto:juan@jvuletich.org">
Juan Vuletich</a>; <a href="mailto:hernan.wilkinson@10pines.com">Hernan Wilkinson</a><br>
<b>Subject: </b>Re: [Cuis-dev] Unwind mechanism during termination is broken and inconsistent</p>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Hi Juan,<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I apologize for responding so late to your great comments. I felt I had to educate myself in error handling first :)<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">> m1<o:p></o:p></p>
<p class="MsoNormal">>    | a |<o:p></o:p></p>
<p class="MsoNormal">>    a := Array new: 3.<o:p></o:p></p>
<p class="MsoNormal">>    self m2: a.<o:p></o:p></p>
<p class="MsoNormal">>    a at: 3 put: true.<o:p></o:p></p>
<p class="MsoNormal">>    a print.<o:p></o:p></p>
<p class="MsoNormal">><o:p> </o:p></p>
<p class="MsoNormal">>  m2: a   "A"<o:p></o:p></p>
<p class="MsoNormal">>    [1 + 2] ensure: [<o:p></o:p></p>
<p class="MsoNormal">>      [ 3 + 4 ] ensure: [    "*1"<o:p></o:p></p>
<p class="MsoNormal">>        true ifTrue: [<o:p></o:p></p>
<p class="MsoNormal">>           ^a at: 1 put: true ]]. "*2"<o:p></o:p></p>
<p class="MsoNormal">>      a at: 2 put: true ]<o:p></o:p></p>
<p class="MsoNormal">><o:p> </o:p></p>
<p class="MsoNormal">>  In this example, the *1ensure is there only to guarantee that *2is ran, even if [3+4] happens to fail. If [3+4] it runs without problems, the result should be exactly the same as:<o:p></o:p></p>
<p class="MsoNormal">><o:p> </o:p></p>
<p class="MsoNormal">>  m2: a   "B"<o:p></o:p></p>
<p class="MsoNormal">>    [1 + 2] ensure: [<o:p></o:p></p>
<p class="MsoNormal">>      3 + 4.<o:p></o:p></p>
<p class="MsoNormal">>      true ifTrue: [<o:p></o:p></p>
<p class="MsoNormal">>        ^a at: 1 put: true ].<o:p></o:p></p>
<p class="MsoNormal">>      a at: 2 put: true ]<o:p></o:p></p>
<p class="MsoNormal">><o:p> </o:p></p>
<p class="MsoNormal">>  Applying the same argument, the result should be the same as:<o:p></o:p></p>
<p class="MsoNormal">><o:p> </o:p></p>
<p class="MsoNormal">>  m2: a   "C"<o:p></o:p></p>
<p class="MsoNormal">>    1 + 2.<o:p></o:p></p>
<p class="MsoNormal">>    3 + 4.<o:p></o:p></p>
<p class="MsoNormal">>    true ifTrue: [<o:p></o:p></p>
<p class="MsoNormal">>      ^a at: 1 put: true ].<o:p></o:p></p>
<p class="MsoNormal">>    a at: 2 put: true<o:p></o:p></p>
<p class="MsoNormal">><o:p> </o:p></p>
<p class="MsoNormal">>  In implementation C it is clear that a second isNil. So, the same should be the case for B and A.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">This is a beautiful example! Thanks a lot, very enlightening. I fully agree, my suggestion was incorrect. The logic you are showing says to leave the block where the non-local return executes without unwinding but to continue unwinding
 after that block. Very interesting!<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">> x := nil.<o:p></o:p></p>
<p class="MsoNormal">> [self error: 'x1'] ensure: [<o:p></o:p></p>
<p class="MsoNormal">>   [self error: 'x2'] ensure: [<o:p></o:p></p>
<p class="MsoNormal">> ​    [self error: 'x3'] ensure: [<o:p></o:p></p>
<p class="MsoNormal">> ​      x:=3].<o:p></o:p></p>
<p class="MsoNormal">> ​    x:=2].<o:p></o:p></p>
<p class="MsoNormal">>   x:=1].<o:p></o:p></p>
<p class="MsoNormal">> x<o:p></o:p></p>
<p class="MsoNormal">><o:p> </o:p></p>
<p class="MsoNormal">> I think this case is very similar to the one above.. For example, if we proceed the first debugger (the x1 error), the x2 debugger opens. If we abandon it, we are abandoning the execution of the first ensured block (that includes the
 x := 1 assignment at the end). So, no assignment is done. I think that the behavior of our fix is correct in this case. No need to simulate "resuming".<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">In this case I'd disagree. Abandoning is currently implemented and interpreted as terminating so I chose to follow this logic and made abandoning equivalent to sending #terminate, meaning the unwind will continue and execute the second
 error, open a second debugger etc. Please try out the enclosed changeset; it also follows the non-local logic you suggested above. We've also discussed the issue with Christoph Thiede here: http://forum.world.st/Solving-multiple-termination-bugs-summary-proposal-tp5128285p5129113.html<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">The main idea behind the extended version of #terminate is using the return value from #runUntilErrorOrReturnFrom: to continue the unwind based on the exception found.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I've also fixed a few minor bug in the current implementation:
<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">1) using suspendedContext variable during unwind is an incorrect approach that may lead to leaving unterminated context chains behind which the garbage collector wouldn't recycle; I'm using a local temp 'top' instead.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">2) I removed the part looking for an 'inner' context halfway through execution - it's unnecessary and theoretically even incorrect - and use outerMost as the unwind context that needs to be completed.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">3) the condition in the middle (`ifNil: [^self]`) became unnecessary after your fix.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">4) there was an issue with infinite recursion in case of #cannotReturn and #doesNotUnderstand - this has been fixed by explicitly excluding the two exceptions from returning.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">5) #cannotReturn generally causes a VM crash if it attempts to return - this is fixed by looping #cannotReturn back to itself and the way out leads via Abandoning the debugger window (even accidental pressing Proceed crashes your image
 so I guess it's worth fixing in every case)<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">6) #runUntilErrorOrReturnFrom: required a slight modification - I suggest either modify it or we can create a copy with this modified behavior; I chose to modify it because no current method would be affected by the change.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">As another example illustrating the changes you can try:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">    a := Array new: 4 withAll: false.<o:p></o:p></p>
<p class="MsoNormal">    p := [<o:p></o:p></p>
<p class="MsoNormal">             [<o:p></o:p></p>
<p class="MsoNormal">            [] ensure: [<o:p></o:p></p>
<p class="MsoNormal">                           [ ^2 ] ensure: [<o:p></o:p></p>
<p class="MsoNormal">                                                      a at: 1 put: true].<o:p></o:p></p>
<p class="MsoNormal">                           a at: 2 put: true]<o:p></o:p></p>
<p class="MsoNormal">                           ] ensure: [a at: 3 put: true].<o:p></o:p></p>
<p class="MsoNormal">                           a at: 4 put: true<o:p></o:p></p>
<p class="MsoNormal">        ] newProcess.<o:p></o:p></p>
<p class="MsoNormal">    p resume.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">It crashes the image without amending #cannotReturn but works well with the enclosed changeset and returns
<o:p></o:p></p>
<p class="MsoNormal">correctly a equal to #(true false true false).<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Also this example is catastrophic but dealt with safely with the new changeset:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">   [^2] newProcess resume<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I haven't built any testcases yet but I definitely plan to if you find this interesting.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Many thanks for discussing this issue; any comments will be greatly appreciated :)<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">best,<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Jaromir<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b>From: </b><a href="mailto:cuis-dev@lists.cuis.st">Juan Vuletich via Cuis-dev</a><br>
<b>Sent: </b>Friday, April 30, 2021 19:32<br>
<b>To: </b><a href="mailto:cuis-dev@lists.cuis.st">Discussion of Cuis Smalltalk</a><br>
<b>Cc: </b><a href="mailto:juan@jvuletich.org">Juan Vuletich</a>; <a href="mailto:mail@jaromir.net">
Jaromir Matas</a>; <a href="mailto:hernan.wilkinson@10pines.com">Hernan Wilkinson</a><br>
<b>Subject: </b>Re: [Cuis-dev] Unwind mechanism during termination is broken and inconsistent<o:p></o:p></p>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Hi Jaromir,<br>
<br>
(inline)<br>
On 4/30/2021 7:01 AM, Jaromir Matas via Cuis-dev wrote: <o:p></o:p></p>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal">Hi Juan,<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">Awesome! I remember the idea of using the result of #runUntilErrorOrReturnFrom crossed my mind for a fleeting moment… and then I lost it ;) With your permission I’d like to use your fix in my Squeak version as well.<o:p></o:p></p>
</blockquote>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black">  <br>
I'm glad you like it. Yes, of course. In these low level details, it is a good thing to have as much common code as possible!<br>
<br>
Besides, in general, all the code we publish for Cuis is MIT license, and free for any use.<br>
<br>
<o:p></o:p></span></p>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal">I’ve tried to rewrite your #<span style="color:black">test1ATerminate
</span>without the method calls – and indeed it passes… and that’s why I missed that – it was too simple; when using sends is where your fix comes to the rescue – THANKS!<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">    | p a |<o:p></o:p></p>
<p class="MsoNormal">    a := Array new: 4 withAll: false.<o:p></o:p></p>
<p class="MsoNormal">    p := [<o:p></o:p></p>
<p class="MsoNormal">             [<o:p></o:p></p>
<p class="MsoNormal">                              [ ] ensure: [<o:p></o:p></p>
<p class="MsoNormal">                                             [Processor activeProcess suspend] ensure: [<o:p></o:p></p>
<p class="MsoNormal">                                                           ^a at: 1 put: true].    "line L1"<o:p></o:p></p>
<p class="MsoNormal">                                             a at: 2 put: true]                     "line L2"<o:p></o:p></p>
<p class="MsoNormal">                              ] ensure: [a at: 3 put: true].<o:p></o:p></p>
<p class="MsoNormal">                              a at: 4 put: true<o:p></o:p></p>
<p class="MsoNormal">        ] newProcess.<o:p></o:p></p>
<p class="MsoNormal">    p resume.<o:p></o:p></p>
<p class="MsoNormal">    Processor yield.<o:p></o:p></p>
<p class="MsoNormal">    "make sure p is suspended and none of the unwind blocks has finished yet"<o:p></o:p></p>
<p class="MsoNormal">    self assert: p isSuspended.<o:p></o:p></p>
<p class="MsoNormal">    a noneSatisfy: [ :b | b ].<o:p></o:p></p>
<p class="MsoNormal">    "now terminate the process and make sure all unwind blocks have finished"<o:p></o:p></p>
<p class="MsoNormal">    p terminate.<o:p></o:p></p>
<p class="MsoNormal">    self assert: p isTerminated.<o:p></o:p></p>
<p class="MsoNormal">    self assert: a first & a third.<o:p></o:p></p>
<p class="MsoNormal">    self assert: (a second | a fourth) not.<o:p></o:p></p>
</blockquote>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
Yes it does. I just thought that a more real-life like test of non local returns should also include actual method calls!<o:p></o:p></span></p>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal">I’d like to raise a question here: I feel the second item, on line L2 should ideally execute too because it’s inside an unwind block halfway through it’s termination. The problem is though the non-local return at line L1 invokes it’s own
 unwind algorithm in #resume:through: which ignores halfway through unwind blocks – the reason for that is #resume:through: operates on the active process’s stack which makes it extremely difficult to unwind halfway through blocks. I tried to apply a similar
 tactics like in termination (control the unwind from another stack) and it works well but it’s very intrusive… I may open a separate discussion on that later to share the results. Do you think it may be worth exploring or it’s just not worth the bother?<o:p></o:p></p>
</blockquote>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black">  <br>
Well. This is not just in the case of process #terminate, right? To play with this without involving process handling, but including actual method calls I just tried this:<br>
<br>
m1<br>
    | a |<br>
    a := Array new: 3.<br>
    self m2: a.<br>
    a at: 3 put: true.<br>
    a print.<br>
<br>
m2: a      "A"<br>
    [1 + 2] ensure: [<br>
        [ 3 + 4 ] ensure: [       "*1"<br>
            true ifTrue: [<br>
                ^a at: 1 put: true ]]. "*2"<br>
        a at: 2 put: true ]<br>
<br>
In this example, the *1ensure is there only to guarantee that *2is ran, even if [3+4] happens to fail. If [3+4] it runs without problems, the result should be exactly the same as:<br>
<br>
m2: a     "B"<br>
    [1 + 2] ensure: [<br>
        3 + 4.<br>
        true ifTrue: [<br>
            ^a at: 1 put: true ].<br>
        a at: 2 put: true ]<br>
<br>
Applying the same argument, the result should be the same as:<br>
<br>
m2: a     "C"<br>
    1 + 2.<br>
    3 + 4.<br>
    true ifTrue: [<br>
        ^a at: 1 put: true ].<br>
    a at: 2 put: true<br>
<br>
In implementation C it is clear that a second isNil. So, the same should be the case for B and A.<br>
<br>
I think that an ensured block should be guaranteed to run without external interference. But if it decides on its own to exit before running all its statements, it is it's own decision.<br>
<br>
<o:p></o:p></span></p>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal">Another issue: I considered using the error part of the result of #runUntilErrorOrReturnFrom: to deal with situations like this (careful – crashes the Cuis image without the terminate fix; with the fix it works “ok”):<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">x := nil.<o:p></o:p></p>
<p class="MsoNormal">[self error: 'x1'] ensure: [<o:p></o:p></p>
<p class="MsoNormal">    [self error: 'x2'] ensure: [<o:p></o:p></p>
<p class="MsoNormal">        [self error: 'x3'] ensure: [<o:p></o:p></p>
<p class="MsoNormal">            x:=3].<o:p></o:p></p>
<p class="MsoNormal">        x:=2].<o:p></o:p></p>
<p class="MsoNormal">    x:=1].<o:p></o:p></p>
<p class="MsoNormal">x<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">Here you have nested errors and the question is: If we abandon the Debugger window, what do we want to see as a result? Without the fix the image crashes badly with unwind errors, with the fix however the Debugger closes without unwinding
 – it’s a consequence of # runUntilErrorOrReturnFrom: behavior – it returns errors rather than opens a debugger (and leaves the decision with the user). So what do we want to see as a result – keep opening debugger windows and abandoning them manually or ignoring
 the errors and executing the assignments? That sounds like “resuming” rather than abandoning to me so at the moment I don’t know and will have to think about it. I just didn’t want to complicate the #terminate prematurely :)<o:p></o:p></p>
</blockquote>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black">  <br>
I think this case is very similar to the one above.. For example, if we proceed the first debugger (the x1 error), the x2 debugger opens. If we abandon it, we are abandoning the execution of the first ensured block (that includes the x := 1 assignment at the
 end). So, no assignment is done. I think that the behavior of our fix is correct in this case. No need to simulate "resuming".<br>
<br>
<o:p></o:p></span></p>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal">Juan, many thanks again, I’ll study your tests and learn from them.<o:p></o:p></p>
</blockquote>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
I'm happy to be of help. There's not nothing in those tests that could be new to you. All I did was to add nested method calls, and add the #resume cases, that already did work with the fix. Let me thank you. You did a great analysis of the issues at hand,
 and your fix is a great contribution.<br>
<br>
I'll integrate it right now. If any further analysis provides additional changes, we'll integrate them too.<br>
 <o:p></o:p></span></p>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal">Best regards,<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">jaromir<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
</blockquote>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="color:black"><br>
Cheers,<o:p></o:p></span></p>
<pre>-- </pre>
<pre>Juan Vuletich</pre>
<pre><a href="http://www.cuis-smalltalk.org">www.cuis-smalltalk.org</a></pre>
<pre><a href="https://github.com/Cuis-Smalltalk/Cuis-Smalltalk-Dev">https://github.com/Cuis-Smalltalk/Cuis-Smalltalk-Dev</a></pre>
<pre><a href="https://github.com/jvuletich">https://github.com/jvuletich</a></pre>
<pre><a href="https://www.linkedin.com/in/juan-vuletich-75611b3">https://www.linkedin.com/in/juan-vuletich-75611b3</a></pre>
<pre>@JuanVuletich</pre>
<p class="MsoNormal"><span style="font-size:10.0pt;font-family:"Courier New";color:black"><o:p> </o:p></span></p>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
</body>
</html>