better unwind & new unwindAndRestart
authorclaus
Wed, 24 Aug 1994 01:08:34 +0200
changeset 141 b530e69052e4
parent 140 186976229029
child 142 c7844287bddf
better unwind & new unwindAndRestart
Context.st
--- a/Context.st	Wed Aug 24 01:08:09 1994 +0200
+++ b/Context.st	Wed Aug 24 01:08:34 1994 +0200
@@ -22,7 +22,7 @@
 COPYRIGHT (c) 1988 by Claus Gittinger
               All Rights Reserved
 
-$Header: /cvs/stx/stx/libbasic/Context.st,v 1.17 1994-08-11 21:35:18 claus Exp $
+$Header: /cvs/stx/stx/libbasic/Context.st,v 1.18 1994-08-23 23:08:34 claus Exp $
 '!
 
 !Context class methodsFor:'documentation'!
@@ -43,7 +43,7 @@
 
 version
 "
-$Header: /cvs/stx/stx/libbasic/Context.st,v 1.17 1994-08-11 21:35:18 claus Exp $
+$Header: /cvs/stx/stx/libbasic/Context.st,v 1.18 1994-08-23 23:08:34 claus Exp $
 "
 !
 
@@ -51,15 +51,29 @@
 "
     Context represents the stack context objects; each message send adds a context
     to a chain, which can be traced back via the sender field. 
-    (The actual implementation uses the machines stack for this, building real contexts 
-     when needed only).
+    (The actual implementation uses the machines stack for this, building real 
+     contexts when needed only).
 
     For both method- and block-contexts, the layout is the same. 
-    For method contexts, the home-field is nil, while for blockcontexts the home-field is either 
-    the context of its surrounding block (i.e. the context of the block, in which the receiving
-    block was created, if its a nested block) or of its method. 
+    For method contexts, the home-field is nil, while for blockcontexts the home-
+    field is either the context of its surrounding block (i.e. the context of the
+    block, in which the receiving block was created, if its a nested block) or of
+    its home method. 
+
+    Contexts of cheap blocks do not have a home context - their home field is 
+    also nil.
 
-    Contexts of cheap blocks do not have a home context - their home field is also nil.
+    Currently, contexts do not contain a reference to the method or block which
+    created it - this is not needed for program execution, but could get the debugger
+    somewhat into trouble: it has to search the class hierarchy for receiver/selector
+    combinations to find the method. This usually works, but fails in case of methods
+    which are not anchored in any class - especially leading to problems with wrapper-
+    and lazy methods. Also, Method>>valueWithReceiver - type of invocations cannot
+    be easily debugged.
+    Therefore, the implementation will be changed in the near future, to include a
+    field for the method/block, and set it in the VM during program execution.
+    (there may be some small performance penalty for this, though).
+
 
     instance variables:
         flags       <SmallInteger>          - used by the VM; never touch.
@@ -71,9 +85,13 @@
         selector    <Symbol>                - the selector of this message
         searchClass <Class>                 - the class, where the message lookup started
                                               (for super sends) or nil, for regular sends.
+
         lineNr      <SmallInteger>          - the position where the context left off
                                               (kind of p-counter)
+
         retValTemp  nil                     - temporary - always nil, when you see the context
+                                              (used in the VM as temporary)
+
         handle      *noObject*              - used by the VM; not accessable, not an object
 
         <indexed>                           - arguments of the send followed by
@@ -96,6 +114,9 @@
 !Context class methodsFor:'signal access'!
 
 invalidReturnSignal
+    "return the signal used when a method is tried to be returned twice
+     or, when some dead context is unwound or restarted."
+
     ^ InvalidReturnSignal
 ! !
 
@@ -124,7 +145,7 @@
 isRecursive
     "return true, if this context is one of a recursive send of the same
      selector to the same receiver before. 
-     Used to detect recursive errors - for example."
+     Used to detect recursive errors or recursive printing - for example."
 
     |c rec cls1 cls2|
 
@@ -150,7 +171,7 @@
         "
         rec := rec + 1.
         rec >= 100000 ifTrue:[
-            'bad context chain' errorPrintNewline.
+            'bad context chain' errorPrintNL.
             ^ true
         ]
     ].
@@ -449,13 +470,14 @@
      which has already returned is about to return again.
      (i.e. about to execute a return from an already returned
       method in a block).
-    We raise a signal here, to allow catching of that exception"
+    We raise a signal here, to allow catching of that situation."
 
-    "in previous versions of ST/X and ST-80, this was no error;
-     comment out the error to get that (old) behavior
+    "
+     in previous versions of ST/X and ST-80, this was no error;
+     comment out the raise to get that (old) behavior
     " 
 " "
-    InvalidReturnSignal raiseRequestWith:returnValue.
+    ^ InvalidReturnSignal raiseRequestWith:returnValue.
 " "
     ^ returnValue
 ! !
@@ -465,88 +487,140 @@
 restart
     "restart the receiver - i.e. the method is evaluated again.
      if the context to restart already died - do nothing.
-     LIMITATION: currently a context can only be restarted by
-     the owning process - not from outside."
+     This is a low level helper for unwindAndRestart.
+
+     NOTICE: 
+         NO unwind actions are performed - this is usually not
+         what you want (see Context>>unwindAndRestart).
+
+     LIMITATION: 
+         currently a context can only be restarted by
+         the owning process - not from outside."
 
     sender isNil ifTrue:[^ nil].
 %{
-    __RESUMECONTEXT(SND_COMMA self, RESTART_VALUE);
+    __RESUMECONTEXT__(SND_COMMA self, RESTART_VALUE, 0);
 
     /* when we reach here, something went wrong */
 %}
 .
-    'restart: context not on calling chain' errorPrintNewline.
     "
      debugging ...
     "
-    self error:'restart: context not on calling chain'.
-    ^ nil
+"
+    'restart: context not on calling chain' errorPrintNL.
+    ^ self error:'restart: context not on calling chain'.
+"
+    "
+     tried to restart a context which is already dead
+     (i.e. the method/block has already executed a return)
+    "
+    ^ InvalidReturnSignal 
+          raiseRequestWith:nil
+          errorString:'restart: context not on calling chain'
 !
 
 return
-    "return from this context with nil.
-     NO unwind actions are performed.
-     LIMITATION: currently a context can only be returned by
-     the owning process - not from outside."
+    "return from this context with nil. I.e. as if it did a ^ nil.
+     NOTICE:
+         NO unwind actions are performed - this is usually not
+         what you want (See Context>>unwind).
+         This is a low level method - a helper for unwind.
 
-    self return:nil
+     LIMITATION: 
+         currently a context can only be returned by
+         the owning process - not from outside."
+
+    ^ self return:nil
 !
 
 return:value
     "return from this context as if it did a '^ value'.
-     NO unwind actions are performed.
-     LIMITATION: currently a context can only be returned by
-     the owning process - not from outside."
+     NOTICE:
+         NO unwind actions are performed- this is usually not
+         what you want (See Context>>unwind:).
+         This is a low level method - a helper for unwind.
+
+     LIMITATION: 
+         currently a context can only be returned by
+         the owning process - not from outside."
 
     sender isNil ifTrue:[^ nil].
 %{
-    __RESUMECONTEXT(SND_COMMA self, value);
+    __RESUMECONTEXT__(SND_COMMA self, value, 0);
 
     /* when we reach here, something went wrong */
 %}
 .
-    'return: context not on calling chain' errorPrintNewline.
     "
      debugging ...
     "
-    self error:'restart: context not on calling chain'.
-    ^ nil
+"
+    'return: context not on calling chain' errorPrintNL.
+    ^ self error:'return: context not on calling chain'.
+"
+    "
+     tried to return a context which is already dead
+     (i.e. the method/block has already executed a return)
+    "
+    ^ InvalidReturnSignal 
+          raiseRequestWith:value 
+          errorString:'return: context not on calling chain'
 !
 
 resume
-    "resume execution in this context.
-     NO unwind actions are performed.
+    "resume execution in this context. I.e. as if the method called
+     last by the receiver did a ^ nil.
      If the context has already returned, do nothing.
-     LIMITATION: currently a context can only be resumed by
-     the owning process - not from outside."
+
+     NOTICE:
+         NO unwind actions are performed (see Context>>unwind).
 
-    self resume:nil
+     LIMITATION: 
+         currently a context can only be resumed by
+         the owning process - not from outside."
+
+    ^ self resume:nil
 !
 
 resume:value
     "resume the receiver - as if it got 'value' from whatever
      it called.
-     If the context has already died - do nothing. 
-     NO unwind actions are performed (see unwind: in this class).
-     LIMITATION: currently a context can only be resumed by
-     the owning process - not from outside."
+     If the context has already returned - do nothing. 
+
+     NOTICE:
+         NO unwind actions are performed (see Context>>unwind:).
+
+     LIMITATION: 
+         currently a context can only be resumed by
+         the owning process - not from outside."
 
     |con|
 
-    "start with this context, find the one below and return from it"
+    "
+     starting with this context, find the one below and return from it
+    "
     con := thisContext.
     [con notNil and:[con sender ~~ self]] whileTrue:[
         con := con sender
     ].
     con isNil ifTrue:[
-        'resume: context not on calling chain' errorPrintNewline.
         "
          debugging ...
         "
-        self error:'resume: context not on calling chain'.
-        ^ nil
+"
+        'resume: context not on calling chain' errorPrintNL.
+        ^ self error:'resume: context not on calling chain'.
+"
+        "
+         tried to continue in context which is already dead
+         (i.e. the method/block has already executed a return)
+        "
+        ^ InvalidReturnSignal 
+              raiseRequestWith:value 
+              errorString:'resume: context not on calling chain'
     ].
-    con return:value
+    ^ con return:value
 !
 
 unwind
@@ -554,10 +628,12 @@
      If the context has already retruned, do nothing.
      Evaluate all unwind-blocks as specified in Block>>valueNowOrOnUnwind:
      and Block>>valueOnUnwindDo: on the way.
-     LIMITATION: currently a context can only be unwound by
-     the owning process - not from outside."
 
-    self unwind:nil
+     LIMITATION: 
+         currently a context can only be unwound by
+         the owning process - not from outside."
+
+    ^ self unwind:nil
 !
 
 unwind:value
@@ -565,36 +641,125 @@
      If the context has already returned , do nothing.
      Evaluate all unwind-blocks as specified in Block>>valueNowOrOnUnwind:
      and Block>>valueOnUnwindDo: on the way.
-     LIMITATION: currently a context can only be unwound by
-     the owning process - not from outside."
+
+     LIMITATION: 
+         currently a context can only be unwound by
+         the owning process - not from outside."
 
     |con sel|
 
-    sender isNil ifTrue:[^ nil].
+    sender isNil ifTrue:[
+        "
+         tried to return to a context which is already dead
+         (i.e. the method/block has already executed a return)
+        "
+        ^ InvalidReturnSignal 
+              raiseRequestWith:value 
+              errorString:'unwind: no sender to unwind to'
+    ].
 
-    "start with this context, moving up"
+    "
+     start with this context, moving up, looking for unwind actions
+    "
     con := thisContext.
     [con notNil and:[con ~~ self]] whileTrue:[
         con isBlockContext ifFalse:[
-
-            "the way we find those unwind contexts seems kludgy ..."
+            "
+             the way we find those unwind contexts seems kludgy ...
+            "
             sel := con selector.
             ((sel == #valueNowOrOnUnwindDo:) or:[sel == #valueOnUnwindDo:]) ifTrue:[
-                "... the way we evaluate the unwind blocks too"
+                "
+                 ... the way we evaluate the unwind blocks too
+                "
                 (con argAt:1) value
             ]
         ].
         con := con sender
     ].
 
-    "this should be avoided"
+    "oops, I am not on the calling chain
+     (should we check for this situation first and NOT evaluate
+      the unwind actions in this case ?)
+    "
     con isNil ifTrue:[
-        'unwind: context not on calling chain' errorPrintNewline.
         "
          debugging ...
         "
-        self error:'unwind: context not on calling chain'.
-        ^ nil
+"
+        'unwind: context not on calling chain' errorPrintNL.
+        ^ self error:'unwind: context not on calling chain'.
+"
+        "
+         tried to return to a context which is already dead
+         (i.e. the method/block has already executed a return)
+        "
+        ^ InvalidReturnSignal 
+              raiseRequestWith:value 
+              errorString:'unwind: context not on calling chain'
     ].
-    self return:value
+    "
+     now, that all unwind-actions are done, I can use the
+     low-level return ...
+    "
+    ^ self return:value
+!
+
+unwindAndRestart
+    "restart the receiver - i.e. the method is evaluated again.
+     if the context to restart already died - do nothing.
+     Evaluate all unwind-blocks as specified in Block>>valueNowOrOnUnwind:
+     and Block>>valueOnUnwindDo: before restarting.
+
+     LIMITATION: 
+         currently a context can only be restarted by
+         the owning process - not from outside."
+
+    |con sel|
+
+    "
+     start with this context, moving up, looking for unwind actions
+    "
+    con := thisContext.
+    [con notNil and:[con ~~ self]] whileTrue:[
+        con isBlockContext ifFalse:[
+            "
+             the way we find those unwind contexts seems kludgy ...
+            "
+            sel := con selector.
+            ((sel == #valueNowOrOnUnwindDo:) or:[sel == #valueOnUnwindDo:]) ifTrue:[
+                "
+                 ... the way we evaluate the unwind blocks too
+                "
+                (con argAt:1) value
+            ]
+        ].
+        con := con sender
+    ].
+
+    "oops, I am not on the calling chain
+     (should we check for this situation first and NOT evaluate
+      the unwind actions in this case ?)
+    "
+    con isNil ifTrue:[
+        "
+         debugging ...
+        "
+"
+        'unwindAndRestart: context not on calling chain' errorPrintNL.
+        ^ self error:'unwindAndRestart: context not on calling chain'.
+"
+        "
+         tried to return to a context which is already dead
+         (i.e. the method/block has already executed a return)
+        "
+        ^ InvalidReturnSignal 
+              raiseRequestWith:nil 
+              errorString:'unwindAndRestart: context not on calling chain'
+    ].
+    "
+     now, that all unwind-actions are done, I can use the
+     low-level restart ...
+    "
+    ^ self restart
 ! !