codefrau / Smalltalk78

A Smalltalk-78 VM in Javascript
https://codefrau.github.io/Smalltalk78/
49 stars 8 forks source link

Fix debugger / stack frame / context access #7

Open codefrau opened 10 months ago

codefrau commented 10 months ago

Here's something reported by @Daningalls a while ago:

I would like some help with the St78 debugger. It’s working pretty well, but it’s inconvenient to debug infinite loop errors, and a couple of features in the debugger cause infinite loops even when the user is not to blame. So I was simply going to cause an infinite loop and debug the debugger, specifically to recognize the situation and then shorten the stack so it’s easy to inspect, consumes less resources, etc. But I was surprised to find that when we get an error, we restart the UI on top of the suspended context, instead of creating a new process and restarting the UI in that process. And this seems to make things more fragile because of the limited process size. I don’t understand enough about how the VM fails out to St78 to make this happen.

The VM has three ways of indicating errors:

  1. Message not understood: when the VM fails to lookup a selector, it will invoke the compiled method found as first entry in the special objects array, typically (SpecialOops◦1) ≡ (Object method: ↪error):

    • Object>>error
      • which invokes self messageNotUnderstood: sel withArgs: args from: sender
        • which sends user notify: 'Message not understood: ' + op
  2. Other errors: the VM sends self error: '<error message>', e.g.

    • self error: 'user interrupt' after pressing Cmd-.
    • self error: 'stack space is low' when the next send would make the stack too deep
    • where Object>>error: then invokes
      • user notify: '<error message>'
  3. Primitive failures: if the primitive fails, the code of the primitive method is executed, e.g.

    • Integer>>/ will send
      • user notify: 'Attempt to divide by 0'
    • user croak is used by many primitive methods that don't have special handling, which in turn will execute
      • self notify: 'A primitive has failed.'
    • UserView>>fileString: does ⇑nil (silent error)

An infinite loop should result in case 2, either because there was a user interrupt, or because the stack space got low due to recursive message sends.

I don't think the VM needs to do anything differently, but the image should do something differently, either in error: or notify:

We could look at the St76 logic, but stacks and processes work differently in St78 (thisContext is the current process which has the linear stack of all "contexts" in the current call chain, and the current "context" is accessed as thisContext current answering the current ProcessFrame (St78's equivalent to ST76's Context). To reduce confusion we may rename thisContext to thisProcess but I'm not sure how great of an idea that is.

codefrau commented 10 months ago

@aadsm ran into this, too, and added some debug helpers to the VM https://github.com/aadsm/Smalltalk78/commit/edca116a8abcb7c243df77671023a901ab89c09f