Got a little time so I went ahead and added the context object to the stackframe such that there is time to experiment and to test.

Here a sample program that demonstrates this feature (intentionally using start, reply and also interacting with a context object which invocation has gone out of scope):

   say .line~right(2)":" getPrefix(.context)"(line=".context~line~right(2)", invocation=".context~invocation", 
name=""".context~name""", a" .context~executable~class~id")" t=.test~new

   say center(" t~m1(.false) ",70,'-')
   r2context=t~m1(.false)-- no reply say center(" t~start(""m1"",.true) 
",70,'-')
   t~start("m1",.true)-- asynchroneous & employ reply call syssleep0.1 -- wait for reply 
thread to end say center(" done. ",70,'-')
   say say "now interacting with the context object from R2's stackframe:" say " 
r2context="r2context~string
   say say "r2context~executable (should create a runtime error):" say 
r2context~executable


   /* 
-------------------------------------------------------------------------- */ 
::class test
   /* 
-------------------------------------------------------------------------- */ 
::method m1
      expose alpha beta
      use arg doReply=.false say .line~right(2)":" getPrefix(.context)"(line=".context~line~right(2)", 
invocation=".context~invocation", name=""".context~name""", a" .context~executable~class~id")" a=42 
self~m2(doReply)
      if var("RESULT")then -- return result if any (REPLY will not return a 
result) return result

   /* 
-------------------------------------------------------------------------- */ 
::method m2
      expose gamma delta
      use arg doReply
      say .line~right(2)":" getPrefix(.context)"(line=".context~line~right(2)", invocation=".context~invocation", name=""".context~name""", a" 
.context~executable~class~id")" b=2442 if doReplythen do say .line":" getPrefix(.context)"*** doReply="doReply", hence doing a REPLY now 
(.context~stackframes~items=".context~stackframes~items")" reply say .line":" getPrefix(.context)"*** doReply="doReply", AFTER doing a REPLY now 
(.context~stackframes~items=".context~stackframes~items")" end call r1
      if doReply=.false then -- if REPLY was run, we cannot return a value 
anymore return result

   /* 
-------------------------------------------------------------------------- */ 
::routine r1
      say .line~right(2)":" getPrefix(.context)"(line=".context~line~right(2)", invocation=".context~invocation", 
name=""".context~name""", a" .context~executable~class~id")" c=.dateTime~new
      return r2()


   /* 
-------------------------------------------------------------------------- */ 
::routine r2
      say .line~right(2)":" getPrefix(.context)"(line=".context~line~right(2)", invocation=".context~invocation", 
name=""".context~name""", a" .context~executable~class~id")" d=.dateTime~new
      sf=.context~stackFrames
      say .line": --->" getPrefix(.context)"dumping context objects from" 
pp(sf~items)"stackframes:" do counter c2 sover sf
         mb=.mutableBuffer~new
         ctxt=s~context-- get context object str="stackframe #" c2":" mb~append("stackframe # 
", c2,": ")
         mb~append(getPrefix(ctxt))
         mb~append(" [", s~traceline~left(30),"]")
         mb~append(" variables: {", dir2str(ctxt~variables),"}")
         say mb
      end return sf[1]~context-- return context object (of this routine) /*
   -------------------------------------------------------------------------- 
*/ ::routine pp
      return "["arg(1)"]" /* 
-------------------------------------------------------------------------- */ ::routine dir2str
      use arg dir
      mb=.MutableBuffer~new
      do counter c idxover dir~allindexes~sort
         mb~append( (c=1)~?('',', ') )
         mb~append(idx,"=","<", minLeft(dir[idx]~string),">" )
      end return mb~string

   /* 
-------------------------------------------------------------------------- */ 
::routine minLeft
      use arg str, maxLen=25 if str~length<=maxLenthen return str
      return str~left(maxLen)"..." /* 
-------------------------------------------------------------------------- */ ::routine 
getPrefix
      use arg context
      return "[R"context~interpreter"T"context~thread"I"context~invocation"]"

and here the output if the attached patch gets applied:

     1: [R1 T1 I1] (line= 1, invocation=1, name="G:\test\orx\scratch\test.rex", 
a Routine)
   ---------------------------- t~m1(.false) ----------------------------
   26: [R1 T1 I2] (line=26, invocation=2, name="M1", a Method)
   36: [R1 T1 I3] (line=36, invocation=3, name="M2", a Method)
   50: [R1 T1 I4] (line=50, invocation=4, name="R1", a Routine)
   57: [R1 T1 I5] (line=57, invocation=5, name="R2", a Routine)
   60: ---> [R1 T1 I5] dumping context objects from [5] stackframes:
   stackframe # 1: [R1 T1 I5] [    59 *-* sf=.context~stackFr] variables: {C2=<1>, CTXT=<a 
RexxContext>, D=<2025-02-19T19:28:05.91200...>, MB=<stackfram
   e # 1: [R1 T1 I5...>, RESULT=<stackframe # 1: [R1 T1 I5...>, S=<    59 *-* 
sf=.context~st...>, SF=<an Array>, STR=<stackframe # 1:>}
   stackframe # 2: [R1 T1 I4] [    52 *-* return r2()        ] variables: 
{C=<2025-02-19T19:28:05.91200...>}
   stackframe # 3: [R1 T1 I3] [    44 *-* call r1            ] variables: {B=<2442>, 
DOREPLY=<0>, SELF=<a TEST>, SUPER=<The Object class>}
   stackframe # 4: [R1 T1 I2] [    28 *-* self~m2(doReply)   ] variables: {A=<42>, 
DOREPLY=<0>, SELF=<a TEST>, SUPER=<The Object class>}
   stackframe # 5: [R1 T1 I1] [     5 *-* r2context=t~m1(.fal] variables: {T=<a 
TEST>}
   ------------------------ t~start("m1",.true) -------------------------
   26: [R1 T2 I6] (line=26, invocation=6, name="M1", a Method)
   36: [R1 T2 I7] (line=36, invocation=7, name="M2", a Method)
   40: [R1 T2 I7] *** doReply=1, hence doing a REPLY now 
(.context~stackframes~items=2)
   42: [R1 T3 I7] *** doReply=1, AFTER doing a REPLY now 
(.context~stackframes~items=1)
   50: [R1 T3 I8] (line=50, invocation=8, name="R1", a Routine)
   57: [R1 T3 I9] (line=57, invocation=9, name="R2", a Routine)
   60: ---> [R1 T3 I9] dumping context objects from [3] stackframes:
   stackframe # 1: [R1 T3 I9] [    59 *-* sf=.context~stackFr] variables: {C2=<1>, CTXT=<a 
RexxContext>, D=<2025-02-19T19:28:05.91800...>, MB=<stackfram
   e # 1: [R1 T3 I9...>, RESULT=<stackframe # 1: [R1 T3 I9...>, S=<    59 *-* 
sf=.context~st...>, SF=<an Array>, STR=<stackframe # 1:>}
   stackframe # 2: [R1 T3 I8] [    52 *-* return r2()        ] variables: 
{C=<2025-02-19T19:28:05.91800...>}
   stackframe # 3: [R1 T3 I7] [    44 *-* call r1            ] variables: {B=<2442>, 
DOREPLY=<1>, SELF=<a TEST>, SUPER=<The Object class>}
   ------------------------------- done. --------------------------------

   now interacting with the context object from R2's stackframe:
       r2context=a RexxContext

   r2context~executable (should create a runtime error):
           *-* Compiled method "EXECUTABLE" with scope "RexxContext".
        17 *-* say r2context~executable
   Error 98 running G:\test\orx\scratch\test.rex line 17:  Execution error.
   Error 98.981:  Target RexxContext is no longer active.

Question 1: are there any open issues with these changes?

Question 2: the StackFrameClass got a new field (context) which I added to live(), liveGeneral(), setupFlatten(). Is this correct? Is there something else that needs to be taken care of and if so what exactly?

---rony



On 16.02.2025 01:29, Rick McGuire wrote:
That's because the activation is no longer valid, so there's no valid information available about the activation.

Rick

On Sat, Feb 15, 2025 at 7:21 PM Rony G. Flatscher <rony.flatsc...@wu.ac.at> 
wrote:

    It is RexxActivation::termination() that will issue a 
contextObject->detach() which nullifies
    the activation vairable such that RexxContext::checkValid() will raise the 
execution error.

    ---rony


    On 16.02.2025 01:16, Rony G. Flatscher wrote:

    Could get the invocation id into the stackframe and callerstackframe.

    As your .context idea is quite intriguing :) I then attempted to add it to 
the
    StackFrameClass using RexxActivation::getContextObject() which at first 
sight seemed to work
    as well. However when fetching it from a stackframe and trying to interact 
with it the
    runtime error "Execution error. Target RexxContext is no longer active." 
occurs.

    The RexxContext methods will issue a checkValid() before proceeding, which 
is defined as:

        void RexxContext::checkValid()
        {
             if (activation == OREF_NULL)
             {
                 reportException(Error_Execution_context_not_active);
             }
        }

    It seems that the backing RexxActivation gets nullified such that placing 
it into a
    StackFrame is of no use ATM.

    ---rony


    On 12.02.2025 16:04, Rick McGuire wrote:
    First of all, RexxActivaion is an RexxInternalObject. Those must NEVER be 
returned as an
    instance by Rexx code. They are not fully functional objects that can be 
seen by Rexx code.
    There are also issues with NativeActivation which is part of the same 
hierarchy with
    RexxActivation and also must never be returned to Rexx code. If you really 
feed the need to
    do this, a better choice would be to return the .Context object associated 
with the
    activation. Buy you need to make this adjustment in every place an 
StackFrame object is
    created, because only RexxActivation ones will have a backing context.

    Rick

    On Wed, Feb 12, 2025 at 9:51 AM Rony G. Flatscher <rony.flatsc...@wu.ac.at> 
wrote:

        Currently, the stackframes do not contain the information to which 
RexxActivation
        (“invocation”) they relate to. As a result, a caller stack frame cannot 
be consulted to
        learn which invocation created the called invocation. This is vital 
information in case
        one creates a tracelog and wishes to analyze exactly the flow of 
control post-mortem
        using a tracelog. Therefore, I would like to add that information to 
stackframes using
        the name “invocation” storing the value of RexxActivation::getIdntfr() 
and adjust
        RexxActivation::createStackFrame() and the StackFrameClass accordingly. 
Would there be
        something else that one needs to pay attention to?

        ---rony

Index: interpreter/classes/StackFrameClass.cpp
===================================================================
--- interpreter/classes/StackFrameClass.cpp     (revision 12925)
+++ interpreter/classes/StackFrameClass.cpp     (working copy)
@@ -89,8 +89,9 @@
  * @param t      A tracing line.
  * @param l      The frame line position (MAX_SIZE indicates no line 
available).
  * @param i      The invocation id.
+ * @param c      The context object.
  */
-StackFrameClass::StackFrameClass(const char *ty, RexxString *n, BaseExecutable 
*e, RexxObject *tg, ArrayClass *a, RexxString *t, size_t l, uint32_t i)
+StackFrameClass::StackFrameClass(const char *ty, RexxString *n, BaseExecutable 
*e, RexxObject *tg, ArrayClass *a, RexxString *t, size_t l, uint32_t i, 
RexxObject *c)
 {
     type = ty;
     name = n;
@@ -106,6 +107,7 @@
     traceLine = t;
     line = l;
     invocation = i;     // supplied by RexxActivation
+    context = c;        // supplied by RexxActivation
 }
 
 
@@ -138,6 +140,7 @@
     memory_mark(arguments);
     memory_mark(target);
     memory_mark(objectVariables);
+    memory_mark(context);
 }
 
 void StackFrameClass::liveGeneral(MarkReason reason)
@@ -151,6 +154,7 @@
     memory_mark_general(arguments);
     memory_mark_general(target);
     memory_mark_general(objectVariables);
+    memory_mark_general(context);
 }
 
 void StackFrameClass::flatten(Envelope *envelope)
@@ -166,6 +170,7 @@
     newThis->arguments = OREF_NULL;
     newThis->target = OREF_NULL;
     newThis->objectVariables = OREF_NULL;
+    newThis->context = OREF_NULL;
 
     cleanUpFlatten
 }
@@ -307,6 +312,16 @@
     return getTraceLine();
 }
 
+ /**
+ * Return the context object.
+ *
+ * @return The current context object.
+ */
+RexxObject *StackFrameClass::getContext()
+{
+    return resultOrNil(context);
+}
+
 /**
  * Default makestring method override
  *
Index: interpreter/classes/StackFrameClass.hpp
===================================================================
--- interpreter/classes/StackFrameClass.hpp     (revision 12925)
+++ interpreter/classes/StackFrameClass.hpp     (working copy)
@@ -54,7 +54,7 @@
 public:
     void *operator new(size_t);
 
-    StackFrameClass(const char *type, RexxString *name, BaseExecutable *p, 
RexxObject *target, ArrayClass *arguments, RexxString *t, size_t l, uint32_t 
invocation);
+    StackFrameClass(const char *type, RexxString *name, BaseExecutable *p, 
RexxObject *target, ArrayClass *arguments, RexxString *t, size_t l, uint32_t 
invocation, RexxObject *context);
     inline StackFrameClass(RESTORETYPE restoreType) { ; };
 
     void live(size_t) override;
@@ -73,6 +73,7 @@
     ArrayClass  *getArguments();
     PackageClass *getPackageObject();
     RexxObject *getInvocation();
+    RexxObject *getContext();
     RexxString *makeString() override;
     RexxString *stringValue() override;
 
@@ -96,6 +97,7 @@
     size_t          line;           // the frame line position (MAX_SIZE 
indicates no line available)
     RexxString *traceLine;          // a tracing line
     uint32_t    invocation;         // invocation/activation id
+    RexxObject *context;            // the context object
 
 };
 
Index: interpreter/concurrency/ActivationFrame.cpp
===================================================================
--- interpreter/concurrency/ActivationFrame.cpp (revision 12925)
+++ interpreter/concurrency/ActivationFrame.cpp (working copy)
@@ -62,6 +62,11 @@
     return activation->getEffectivePackageObject();
 }
 
+RexxObject *RexxActivationFrame::getContextObject()
+{
+    return activation->getContextObject();
+}
+
 RexxString *NativeActivationFrame::messageName()
 {
     return activation->getMessageName();
@@ -99,7 +104,7 @@
 
     RexxString *message = 
activity->buildMessage(Message_Translations_compiled_method_invocation, info);
     p = message;
-    return new StackFrameClass(StackFrameClass::FRAME_METHOD, name, 
frameMethod, target, new_array(count, argPtr), message, SIZE_MAX, 0);
+    return new StackFrameClass(StackFrameClass::FRAME_METHOD, name, 
frameMethod, target, new_array(count, argPtr), message, SIZE_MAX, 0, OREF_NULL);
 }
 
 PackageClass *InternalActivationFrame::getPackage()
Index: interpreter/concurrency/ActivationFrame.hpp
===================================================================
--- interpreter/concurrency/ActivationFrame.hpp (revision 12924)
+++ interpreter/concurrency/ActivationFrame.hpp (working copy)
@@ -108,6 +108,7 @@
     BaseExecutable *executable() override;
     StackFrameClass *createStackFrame() override;
     PackageClass *getPackage() override;
+    RexxObject *getContextObject();
 
  protected:
 
Index: interpreter/execution/CPPCode.cpp
===================================================================
--- interpreter/execution/CPPCode.cpp   (revision 12925)
+++ interpreter/execution/CPPCode.cpp   (working copy)
@@ -1269,6 +1269,7 @@
     CPPM(StackFrameClass::getExecutable),
     CPPM(StackFrameClass::getLine),
     CPPM(StackFrameClass::getInvocation),
+    CPPM(StackFrameClass::getContext),
     CPPM(StackFrameClass::getTraceLine),
     CPPM(StackFrameClass::getType),
     CPPM(StackFrameClass::getTarget),
Index: interpreter/execution/NativeActivation.cpp
===================================================================
--- interpreter/execution/NativeActivation.cpp  (revision 12925)
+++ interpreter/execution/NativeActivation.cpp  (working copy)
@@ -3624,9 +3624,12 @@
         ArrayClass *info = new_array(getMessageName());
         ProtectedObject p(info);
 
+        RexxObject *context = activation->getContextObject();
+        ProtectedObject p2(context);
+
         RexxString *message = 
activity->buildMessage(Message_Translations_compiled_routine_invocation, info);
         p = message;
-        return new StackFrameClass(StackFrameClass::FRAME_ROUTINE, 
getMessageName(), (BaseExecutable *)getExecutableObject(), NULL, 
getArguments(), message, SIZE_MAX, 0);
+        return new StackFrameClass(StackFrameClass::FRAME_ROUTINE, 
getMessageName(), (BaseExecutable *)getExecutableObject(), NULL, 
getArguments(), message, SIZE_MAX, 0, context);
     }
     else
     {
@@ -3633,9 +3636,12 @@
         ArrayClass *info = new_array(getMessageName(), ((MethodClass 
*)executable)->getScopeName());
         ProtectedObject p(info);
 
+        RexxObject *context = activation->getContextObject();
+        ProtectedObject p2(context);
+
         RexxString *message = 
activity->buildMessage(Message_Translations_compiled_method_invocation, info);
         p = message;
-        return new StackFrameClass(StackFrameClass::FRAME_METHOD, 
getMessageName(), (BaseExecutable *)getExecutableObject(), receiver, 
getArguments(), message, SIZE_MAX, 0);
+        return new StackFrameClass(StackFrameClass::FRAME_METHOD, 
getMessageName(), (BaseExecutable *)getExecutableObject(), receiver, 
getArguments(), message, SIZE_MAX, 0, context);
     }
 }
 
Index: interpreter/execution/NativeActivation.hpp
===================================================================
--- interpreter/execution/NativeActivation.hpp  (revision 12924)
+++ interpreter/execution/NativeActivation.hpp  (working copy)
@@ -100,6 +100,7 @@
     RexxActivation *findRexxContext() override;
     RexxObject *getReceiver() override;
     PackageClass *getPackage() override { return getPackageObject(); }
+    RexxObject *getContextObject() { return getContextObject(); }
     SecurityManager *getSecurityManager() override;
     const NumericSettings *getNumericSettings() override;
 
Index: interpreter/execution/RexxActivation.cpp
===================================================================
--- interpreter/execution/RexxActivation.cpp    (revision 12925)
+++ interpreter/execution/RexxActivation.cpp    (working copy)
@@ -5017,7 +5017,7 @@
     // arguments.
     RexxString *traceback = getTraceBack();
 
-    return new StackFrameClass(type, getMessageName(), getExecutableObject(), 
target, arguments, traceback, getContextLineNumber(), getIdntfr());
+    return new StackFrameClass(type, getMessageName(), getExecutableObject(), 
target, arguments, traceback, getContextLineNumber(), getIdntfr(), 
getContextObject());
 }
 
 /**
Index: interpreter/memory/Setup.cpp
===================================================================
--- interpreter/memory/Setup.cpp        (revision 12925)
+++ interpreter/memory/Setup.cpp        (working copy)
@@ -1705,6 +1705,7 @@
         AddMethod("Executable", StackFrameClass::getExecutable, 0);
         AddMethod("Line", StackFrameClass::getLine, 0);
         AddMethod("Invocation", StackFrameClass::getInvocation, 0);
+        AddMethod("Context", StackFrameClass::getContext, 0);
         AddMethod("Target", StackFrameClass::getTarget, 0);
         AddMethod("TraceLine", StackFrameClass::getTraceLine, 0);
         AddMethod("Type", StackFrameClass::getType, 0);
Index: interpreter/parser/LanguageParser.cpp
===================================================================
--- interpreter/parser/LanguageParser.cpp       (revision 12925)
+++ interpreter/parser/LanguageParser.cpp       (working copy)
@@ -868,7 +868,7 @@
     RexxString *traceback = package->traceBack(OREF_NULL, clauseLocation, 0, 
true);
     ProtectedObject p(traceback);
     return new StackFrameClass(StackFrameClass::FRAME_COMPILE, 
package->programName, OREF_NULL,
-        OREF_NULL, OREF_NULL, traceback, clauseLocation.getLineNumber(), 0);
+        OREF_NULL, OREF_NULL, traceback, clauseLocation.getLineNumber(), 0, 
OREF_NULL);
 }
 
 
_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to