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