RFE: <https://sourceforge.net/p/oorexx/feature-requests/849/>

Commits:

 * docs: <https://sourceforge.net/p/oorexx/code-0/12930/>
 * tests: <https://sourceforge.net/p/oorexx/code-0/12931/>
 * code: <https://sourceforge.net/p/oorexx/code-0/12932/>

All tests pass on Windows.

---rony



On 19.02.2025 19:29, Rony G. Flatscher wrote:

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

_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to