Question
Evgeny Shvarov · Dec 10, 2021

Is it possible to change the code of a class programmatically?

Hi folks!

I'm curious if it is possible to change the class method to another class?

I call a method of a system class  to generate another class. But there is a bug/feature in a library class, so I need to tweak a bit the generated method, e.g. add additional parameter and change the line to "if" the parameter.

I hope to do this programmatically after class generation and change the code in a way I like.

What are the options?

Product version: IRIS 2021.1
2
2 256
Discussion (7)1
Log in or sign up to continue

Sure!  very condensed

  • set cls=##class(%Dictionary.ClassDefinition).%OpenId("<pkg>.<classname>")
  • set met=cls.Methods.GetAt(1)  ; may need some search
  • set imp=met.Implementation   ; a GlobalChar.Stream
  • write imp,OutputToDevice()

or you jump to the Method directly

  • set met=##class(%Dictionary.MethodDefinition).%OpenId("<pkg>.<classname>||<methodname>")

And these classes have tons of params to set or clear by specific methods

Thanks, Robert!

And what is the easiest way to change the particular line of code?

E.g. I need to add a parameter to a method definition.

No brilliant idea yet:

  • disassemble the char.stream to lines in steps of 10 to PPG
  • insert new lines in between
  • reassemble the stream by $o() from PPG

not so impressive.

If you mean an argument to the Method, use the MethDef.FormalSpec string:
The format is Arg1:Type,Arg2:Type=default,...

Open the ClassDefinition: Set ClassDef=##class(%Dictionalry.ClassDefinition).%OpenId(ClassName)
Add the Parameter

Set Parm=##class(%Dictionary.ParameterDefinition).%New(ClassName_":"_ParameterName)
Set Parm.Default=ParameterValue
Do ClassDef.Parameters.Insert(Parm)


Find the desired method in ClassDef.Methods collection by MethDef.Name
Use the MethDef.Implementation stream to manipulate the code
Compile the class.

Yeah, of course, you can change any class, if it's not a system and not deployed class stored in a read-only database

looks like you already know how to generate classes, so, to edit some method, you have to open a particular method by its id, which can be constructed from the class name and the method name.

USER>set method = ##class(%Dictionary.MethodDefinition).%OpenId("%Library.File||Exists") 

USER>write method.Implementation.Size
56

Thanks @Dmitry Maslennikov, @Robert C. Cemper and @Herman Slagman !

Turn out it's not that hard with your help!

The result can be observed here - adding a parameter and the implementation to a method in csvgen.

And yes, here is the code itself:

ClassMethod AddAppendtoImport(pClassName As %String) As %Status
{
 
    set method = ##class(%Dictionary.MethodDefinition).%OpenId(pClassName_"||Import")

    if method.FormalSpec["pAppend" quit $$$OK
    set method.FormalSpec=method.FormalSpec_",pAppend:%Boolean=0"
    set imp=method.Implementation
    set newImp=##class(%Stream.TmpCharacter).%New()

    while 'imp.AtEnd {
        set line=imp.ReadLine() 
        if line'["%DeleteExtent" {do newImp.WriteLine(line)}
        else {
            do newImp.WriteLine($p(line,"do")_"if 'pAppend do ..%DeleteExtent(,.tDeleted,.tInstances,1)")
        }
        }
    set method.Implementation=newImp
    set sc=method.%Save() 
    if $$$ISERR(sc) return sc
    set sc=$System.OBJ.Compile(pClassName,"cuk")
    return sc
}