Question
· Jan 19

Where did my variables go? Without Exclusive NEW, now I get <UNDEFINED>

I inherited some legacy MUMPS / ObjectScript code. A code review identified an exclusive NEW and that is not allowed per our coding standards.

 

The original code contains:

RUN(CALL,DRVNAME)
 S $EC=""
 S ^TMP($J,"RMPV","DRVNAME")=DRVNAME
 N TMPFILE,OLDIO
 S OLDIO=$IO
 I $G(^TMP("RMPV","SILENT"),1) S IOP="NULL",%ZIS=0 D ^%ZIS I '$G(POP,1) U IO
 D INIT^@DRVNAME
 D  ; scope variables
 .N (DUZ,CALL) ; Protect %response
 .S IOF="""""",IOM=80,U="^"
 .D @CALL
 U OLDIO
 Q

 

I attempted to replace Exclusive NEW with a ClassMethod in a procedure block as follows:

RUN(CALL,DRVNAME) ;primary entry point
 Set ^OLIVER(0-$I(^OLIVER)) = $ZDateTime($NOW(),8,1,3)_" : "_$JOB_"/"_$STACK_" : RUN1^RMPVDRV"
 Set ^OLIVER(0-$G(^OLIVER),"CALL")=CALL
 Set ^OLIVER(0-$G(^OLIVER),"DRVNAME")=DRVNAME
 Merge ^OLIVER(0-$G(^OLIVER),"DUZ")=DUZ
 If '$Data(DUZ) Set DUZ=$Get(^OLIVER)
 If '$D(^OLIVER) $EC=""
 ^TMP($J,"RMPV","DRVNAME")=DRVNAME
 TMPFILE,OLDIO
 OLDIO543=$IO
 $G(^TMP("RMPV","SILENT"),1) IOP="NULL",%ZIS=0 ^%ZIS '$G(POP,1) IO
 Set ^OLIVER(0-$G(^OLIVER),"SILENT")=$G(^TMP("RMPV","SILENT"),1)
 INIT^@DRVNAME
 Set ^OLIVER(0-$I(^OLIVER),"OLDIO1")=OLDIO543
 //D ; scope variables
 //.;N (DUZ,CALL) ; Protect %response
 //.S IOF="""""",IOM=80,U="^"
 //.Do @CALL
 Do ##class(VALIP.REST.Implementation.Generic).doCall(CALL,DUZ)
 Set ^OLIVER(0-$I(^OLIVER)) = $ZDateTime($NOW(),8,1,3)_" : "_$JOB_"/"_$STACK_" : RUN2^RMPVDRV"
 Set ^OLIVER(0-$I(^OLIVER),"OLDIO2 in RMPVDRV")=$Get(OLDIO543)
 OLDIO543
 Q

I see <UNDEFINED> error when I try to OLDIO543. All my variables have gone. What happened? Or how can I replace Exclusive NEW?

 

Here is the class with my new ClassMethod:

Class VALIP.REST.Implementation.Generic [ ProcedureBlock ]
{

/// Protect %response
ClassMethod doCall(CALL, DUZ) [ PublicList = (CALL, DUZ) ]
{
Try {
Set IOF="""""",IOM=80,U="^"
Do @CALL
Quit
Catch exp {
Set ^OLIVER(0-$I(^OLIVER)) = $ZDateTime($NOW(),8,1,3)_" : "_$JOB_" : Catch in VALIP.REST.Implementation.Generic"
Set ^OLIVER(0-$G(^OLIVER),"$ZERROR") = $ZERROR
Set ^OLIVER(0-$G(^OLIVER),"exp") = exp
}
Quit
}

}
 

Product version: IRIS 2022.1
$ZV: IRIS for UNIX (Red Hat Enterprise Linux 8 for x86-64) 2022.1 (Build 209_0_22699U) Mon Aug 14 2023 10:39:21 EDT [Health:3.5.0]
Discussion (3)1
Log in or sign up to continue

Some comments (and please excuse the rude comments) before my answer
1) in general, it's never a good idea, to enforce a "new coding standard" for an old code. This is especially valid for old MUMPS and old ObjectScript code - except, you redesign or at least rework, the entire codebase.
2) use the Codesnippet button to write a more readable code (it's not necessary, but it helps us)
3) include only relevant parts/lines of code, in the above code, all that ^OLIVERs are completely irrelevant and makes the reading and understanding of an foreigen code more difficult, with other words, help us to help you!

And now to your problem. The old code is written in a mode, which we call "non procedural" code, i.e. all variables are public except the NEWed (which are invisible). The original code changes or kills at least the OLDIO543 variable, somewhere in the called part (do @CALL), and this fact is considered with the

 N TMPFILE,OLDIO
 S OLDIO=$IO
 I $G(^TMP("RMPV","SILENT"),1) S IOP="NULL",%ZIS=0 D ^%ZIS I '$G(POP,1) U IO
 D INIT^@DRVNAME
 D  ; scope variables
 .N (DUZ,CALL) ; Protect %response  // <-- save all except DUZ and call
 .S IOF="""""",IOM=80,U="^"
 .D @CALL
 U OLDIO

To move all commands from argumentless DO into a classmethod is OK, but you can't change a "non procedural" flow to a "procedural"!
So the solution is, you leave the old NEW as is and define your class method as:

ClassMethod doCall() [ ProcedureBlock=0 ]
{
 ...
}

If you want to keep your procedural code then you have to rework the code, which is called by "do @CALL" too and probably the code which is called from inside of "DO @CALL". Good luck!

Hi, Julius. Thank you for responding to my question.

I work for Department of Veterans Affairs. I believe the coding standard including prohibiting argumentless or exclusive NEW is not new.

Someone modified old code to become non-interactive. Unfortunately they did not follow coding standard and now it is up to me to fix it. I found where variables are killed.

I think a class method using procedure block should be equivalent to exclusive new. All variables are hidden except parameters or public list.